Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Статический экземпляр класса в шаблоне


Автор: Tiarwe 1.1.2014, 17:14
Здравствуйте!
Есть шаблонный класс. Внутри у него вложенный класс. Требуется создать внутри статический член этого вложенного класса.

Пытался так:
Код

template<class T>
class A
{
public:
    class B
    {
    public:
    };

    static B    mB;
};

template<class T>
A<T>::B A<T>::B::mB;


Но при компиляции ошибки:
Цитата

Warning    1    warning C4346: 'A<T>::B' : dependent name is not a type
Error    2    error C2143: syntax error : missing ';' before 'A<T>::B::mB'    
Error    3    error C1903: unable to recover from previous error(s); stopping compilation


Как можно создать статический экземпляр вложенного в шаблон класса? И возможно ли это вообще?

Автор: mabrarov 1.1.2014, 19:17
Цитата(Tiarwe @ 1.1.2014,  17:14)
Warning    1    warning C4346: 'A<T>::B' : dependent name is not a type
Error    2    error C2143: syntax error : missing ';' before 'A<T>::B::mB'    
Error    3    error C1903: unable to recover from previous error(s); stopping compilation

Use typename, Luke!
Код
template<typename T>
class A
{
public:
  class B
  {
  public:
  };

  static B mB;
};

template<typename T>
typename A<T>::B A<T>::mB;

И это еще не создание статического члена, а лишь описание того, как он должен создаваться при специализации шаблонного класса A.

Автор: akizelokro 2.1.2014, 19:35
Код

  static B mB;

ты ещё даже класс B не объявил, а уже используешь в объявлении. как я понял. Как я понимаю, С++ уже зашивается 9в силу ряда причин на таких примерах), да и логичней было бы в любом случае экземпляр класса B вынести хотя бы в класс A

Автор: Lukkoye 3.1.2014, 18:52
Цитата(akizelokro @  2.1.2014,  19:35 Найти цитируемый пост)
ты ещё даже класс B не объявил, а уже используешь в объявлении. как я понял. Как я понимаю, С++ уже зашивается 9в силу ряда причин на таких примерах), да и логичней было бы в любом случае экземпляр класса B вынести хотя бы в класс A 


Не верно. Класс B уже известен.
Ошибка была в синтаксисе. Правильный ответ во втором сообщении этой темы.

Автор: akizelokro 6.1.2014, 16:31
Цитата(Lukkoye @  3.1.2014,  18:52 Найти цитируемый пост)
Правильный ответ во втором сообщении этой темы. 


Угу. Но я, походу, ещё никак С++ 11 не освою.  smile 

Автор: vinter 6.1.2014, 17:44
Цитата(akizelokro @  6.1.2014,  17:31 Найти цитируемый пост)
Угу. Но я, походу, ещё никак С++ 11 не освою.

Здесь нет кода, который требует функционал C++11

Автор: akizelokro 6.1.2014, 20:37
Цитата(vinter @  6.1.2014,  17:44 Найти цитируемый пост)
Здесь нет кода, который требует функционал C++11 


Есть. Я даже подноготную рассмотрел 6-летней давности для применения typename и class. Начиная от исторических ссылок, вариантов применения в различных компляторов и тра-ля-ля ещё что-то там. Вообще сам typename и характер его применения сформировался именно на подходе к С++ 11. Я даже с примерами рассмотрел (я не тот чингачгук, который два раза на одни грабли наступает).

Вообще, я так понял, что вообще желательно typename пихать в код куда надо и, куда, как, казалось бы, не надо. И маложелательно употреблять class (вроде новомодная тенденция? )


Автор: Lukkoye 6.1.2014, 22:27
Цитата(akizelokro @  6.1.2014,  20:37 Найти цитируемый пост)
Вообще, я так понял, что вообще желательно typename пихать в код куда надо и, куда, как, казалось бы, не надо. И маложелательно употреблять class (вроде новомодная тенденция? )


Можно так:
http://codepad.org/x2LKMHsw


Код

template<class T>struct A
{
  struct B {};
  static B mB;
};
template<class T> struct A<T>::B A<T>::mB;

int main()
{
   A<int> a;
}



По поводу использования typename неплохо написала Елена Сагалева в своем блоге:
http://alenacpp.blogspot.ru/2006/08/typename.html


От себя я могу лишь добавить: на моей практике наиболее частое использование typename  - это typedef в шаблонах, при метапрограммировании.

Я покажу вам простейший пример:

http://rextester.com/CEC63398

Код

#include<typeinfo>
#include<iostream>
using namespace std;

//---------вернет true, если типы одинаковые------//
template <typename T, typename U> struct IsEqual { enum { value = false }; };
template <typename T> struct IsEqual<T,T> { enum { value = true }; };
//------------------------------------------------//

//--------------------если T равно U, то результат будет D, а иначе - G------------//
template<class T, class U, class D, class G> struct TSelectIF  { typedef G Result_t; };
template<class T, class D, class G>  struct TSelectIF<T,T,D,G> { typedef D Result_t; };
//------------------------------------------------//

struct SEmpty{};

template<class T>struct Test
{
    // --- мы используем ключевое слово 'typename'
    // --- что бы сообщить компилятору, что Result_t - это имя типа, 
    // --- которое зависит от параметров шаблона TSelectIF
    typedef typename TSelectIF<T, void, SEmpty,T>::Result_t Type;
};

int main()
{
    typedef Test<void>::Type Type1;
    cout<<"class name: "<<typeid(Type1).name()<<endl;
    
    const bool is_sempty1 = IsEqual<Type1, SEmpty>::value;
    cout<< "is sempty? "<< ((is_sempty1)? "yes":"not")<<endl;
    
    //------//    
    
    typedef Test<int>::Type Type2;
    cout<<"class name: "<<typeid(Type2).name()<<endl;
    
    const bool is_sempty2 = IsEqual<Type2, SEmpty>::value;
    cout<< "is sempty? "<< ((is_sempty2)? "yes":"not")<<endl;
}



Здесь, в зависимости от того, чем будет Т в шаблоне:
template<class T>struct Test

Его внутренний:
typedef typename TSelectIF<T, void, SEmpty,T>::Result_t Type;

Будет принимать значение либо SEmpty, либо T

Но известно это станет лишь в момент инстанцирования шаблона, а до этого момента, компилятору нужно что-то сделать с этой записью: понять, чему именно сопоставляется имя типа Type:

typedef  ...что-то-непонятное... Type;  

Мы указываем typename, давая понять компилятору, что там - некий тип данных, который зависит от параметров родительского шаблона, и поэтому станет известен позже - в момент инстанцирования.

Автор: akizelokro 7.1.2014, 07:13
Есть ещё неплохая статья на хабре.
И, не помню уже где, но идёт предположение, что некоторые компиляторы требуют typename даже с некоторой избыточностью. И вами не указаны примеры, навроде "классического", как я понимаю:

template <typename T> some_function()
{
  ...
  typename T::iterator it; // iterator может быть stl-ским итератором, а может быть просто членом класса T
  ...
  // простые примеры с разночтениями списков инициализаций и определениями пустых функций
}
одними typedef применение typename не ограничивается. Просто мы сейчас рассматриваем достаточно простые примеры с шаблонами, например, функций в одну строку.

В MSVS этот пример не компилится совсем.
Код

template<class T>struct A
{
  struct B {}; // непонятно, что будет в зависимости от компилятора. объявление или инициализация?
  static B mB;
};
template<class T> struct A<T>::B A<T>::mB;


И этот не компилится

Код

template<class T>struct A
{
  struct B {};
  static B mB;
};
template<class T> struct A<T>::B A<T>::mB;


А этот вызывает внутренную ошибку компилятора.
Код

template<class T>
struct A
{
public:
    struct B {
//    public:
    };

  static struct B mB;
 
};
template<class T> typename struct A<T>::B A<T>::mB;

Думаю, что struct из-за двоякости их определения (можно указывать struct или нет в ряде выражений, или так мне только кажется), несут известный риск при применении

Автор: vinter 7.1.2014, 08:33
Цитата(akizelokro @  6.1.2014,  21:37 Найти цитируемый пост)
Есть. Я даже подноготную рассмотрел 6-летней давности для применения typename и class. Начиная от исторических ссылок, вариантов применения в различных компляторов и тра-ля-ля ещё что-то там. Вообще сам typename и характер его применения сформировался именно на подходе к С++ 11. Я даже с примерами рассмотрел (я не тот чингачгук, который два раза на одни грабли наступает).

я не знаю о чём ты говоришь, никаких изменений касающихся ключевого слова typename в C++11 нет.

Цитата(akizelokro @  7.1.2014,  08:13 Найти цитируемый пост)
Есть ещё неплохая статья на хабре.И, не помню уже где, но идёт предположение, что некоторые компиляторы требуют typename даже с некоторой избыточностью. И вами не указаны примеры, навроде "классического", как я понимаю:

На заборе тоже написано. Это видимо те же "предполагатели", что к гадалкам ходят.
Цитата(akizelokro @  7.1.2014,  08:13 Найти цитируемый пост)
В MSVS этот пример не компилится совсем.

Потому, что это некорректный C++ код. Как он должен компилироваться?
Код

template<class T>
struct A
{
    struct B {};
    static B mB;
};

template<class T> typename A<T>::B A<T>::mB;

Цитата(akizelokro @  7.1.2014,  08:13 Найти цитируемый пост)
Думаю, что struct из-за двоякости их определения (можно указывать struct или нет в ряде выражений, или так мне только кажется), несут известный риск при применении

У тебя каша в голове, struct используется только при декларации\определении структуры. Больше его совать никуда не стоит - оно там быть не должно.

Автор: vinter 7.1.2014, 08:56
Посмотри эту http://www.ozon.ru/context/detail/id/3960662/, там есть всё, что нужно знать про шаблоны. Правда без новшеств, типа variadic templates, но книга всё равно должна помочь в понимании.

Автор: akizelokro 7.1.2014, 09:29
Цитата(vinter @  7.1.2014,  08:33 Найти цитируемый пост)
Потому, что это некорректный C++ код. Как он должен компилироваться?


Не компилится другой пример, а указанный вами как раз и компилится.

Цитата(vinter @  7.1.2014,  08:33 Найти цитируемый пост)
У тебя каша в голове, struct используется только при декларации\определении структуры. Больше его совать никуда не стоит - оно там быть не должно.


Насколько я понял (могу и ошибаться), в C++ 14 будет возможна и инициализация вида

struct B { int x = 5, int y = 3; };

или в куче материала я просто увидел такую хотелку, уже прямо так сразу и не вспомнить.

Цитата(vinter @  7.1.2014,  08:33 Найти цитируемый пост)
У тебя каша в голове, struct используется только при декларации\определении структуры. Больше его совать никуда не стоит - оно там быть не должно.
.

Ну, ещё, отбросив предыдущий, возможно некорректный пример, статические постоянные члены struct могут проинициализированы при декларации/определении. Также инициализацию списком подцепляют и к декларации. Также, осталось и, кажись наследство С в виде 

Код

struct B c;


Видел и такие конструкции. А всё что не запрещено, - разрешено.

Но в голове каша реальная вырисовывается. Потому ещё не все, как я понял, компиляторы, реализовали полностью нововведения из С++ 11, а тут ещё на подходе С++ 14, который тоже начнут реализовывать в непонятной последовательности пунктов. 
А за ссылку - спасибо.

Автор: vinter 7.1.2014, 09:35
Цитата(akizelokro @  7.1.2014,  10:29 Найти цитируемый пост)
Насколько я понял (могу и ошибаться), в C++ 14 будет возможна и инициализация вида

она уже есть(в C++11). Но у тебя же код, который явно означает определение структуры:
Код

struct B {};

Чтобы это стало инициализацией это должно выглядеть так:
Код

struct B b{};

Цитата(akizelokro @  7.1.2014,  10:29 Найти цитируемый пост)
Потому ещё не все, как я понял, компиляторы, реализовали полностью нововведения из С++ 11, а тут ещё на подходе С++ 14, который тоже начнут реализовывать в непонятной последовательности пунктов. 

В clang полностью реализован C++14

Автор: akizelokro 7.1.2014, 12:33
Цитата(vinter @  7.1.2014,  09:35 Найти цитируемый пост)
В clang полностью реализован C++14 


Каша намечается забавная! 
Всё-таки в ООП схеме
Код


class CPlusPlus : class C
{
  ...
}


наследование от С - более азартная вещь, чем 100%-ООП парадигма  smile 

Автор: bsa 7.1.2014, 12:49
Цитата(Tiarwe @  1.1.2014,  18:14 Найти цитируемый пост)
Требуется создать внутри статический член этого вложенного класса.

Напрямую сделать это нельзя. Можно сделать статический метод, который содержит объявление статической переменной и возвращает ссылку на нее.
Код
template<class T>
class A
{
public:
    class B
    {
    public:
    };

    static B& getB() { static B    mB; return mB; }
};

Автор: Lukkoye 7.1.2014, 13:21
Цитата(bsa @  7.1.2014,  12:49 Найти цитируемый пост)
Напрямую сделать это нельзя. Можно сделать статический метод, который содержит объявление статической переменной и возвращает ссылку на нее.


Я на самом деле так же делаю, по двум причинам:

1. Код проще написать, и проще потом прочитать.
2. Независимость от порядка инициализации единиц трансляции.


Однако, довольно странно читать "напрямую сделать это нельзя" , когда незадолго до вас уже указали способ, "как это сделать можно".

Автор: vinter 7.1.2014, 13:22
Цитата(bsa @  7.1.2014,  13:49 Найти цитируемый пост)
Напрямую сделать это нельзя

Можно, и самый первый ответ в этой теме является верным.

Автор: akizelokro 7.1.2014, 14:05
Код

template<class T>
class A
{
public:
    class B
    {
    public:
    };
    static B& getB() { static B    mB; return mB; }
};

Цитата(vinter @  7.1.2014,  13:22 Найти цитируемый пост)

Можно, и самый первый ответ в этой теме является верным. 


Вообще-то, это последняя версия класса Singleton

class Singleton 
{
  public:
  static Singleton & getInstance()
  {
    static singleton s;
    return s;
  }
  private:

  //  constructors and destructors
}

Добавлено @ 14:10
 Здесь вообще конфронтация. Изначальная формулировка задачи 
"Требуется создать внутри статический член этого вложенного класса.".
Дырбулышург.
Делайте вложенным классом подобие Singleton, для разнообразия попробуйте его как static class
И очень многое получите.

А вообще, мне просто не было надобности работать с С++ и с шаблонами, тоже мне задача, я работал на C++ как с чистым Cшником. Поэтому не было так весело! smile 

Автор: akizelokro 7.1.2014, 14:22
Вообще, щас пики забивают на thread-safe экземпляр класса Singleton (спорят, какая реализация будет потокобезопасной).
Я так спьяну глянул, и понял, что будет последняя версия (которую я зачтил), и набросок которой привёл bsa/
Остальные пусть ждут стандартной версии компирела.

Меня же больше в примере с singleton заинтересовали не откровения кучи прогеров и не приблуды, которыми они его в разных версиях компилеров пытались "расшифровать", а N-тоны (Double-tone, Three-tone and so on)

Автор: bsa 9.1.2014, 10:52
Цитата(Lukkoye @  7.1.2014,  14:21 Найти цитируемый пост)
Однако, довольно странно читать "напрямую сделать это нельзя" , когда незадолго до вас уже указали способ, "как это сделать можно". 
И что, проблем не будет при подключении хидера в разных единицах трансляции и инстанцировании в них с одинаковыми параметрами шаблона? По идее, в этом случае линкер должен будет ругаться на множественное определение символа.
akizelokro, мой пример - это не зачаток "singleton". Просто в singleton используется тот же механизм.

Автор: vinter 9.1.2014, 14:15
Цитата(bsa @  9.1.2014,  11:52 Найти цитируемый пост)
И что, проблем не будет при подключении хидера в разных единицах трансляции и инстанцировании в них с одинаковыми параметрами шаблона? По идее, в этом случае линкер должен будет ругаться на множественное определение символа.

В данном случае, задача линкера заткнуться и свести все эти инстанциации в одну. Тут тот же случай, что и с функциями. Тут нет явной инстанциации в заголовке, а значить ODR не нарушается.

Добавлено @ 14:19
Цитата(14.6.4.1/7)

A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. A specialization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required

Автор: Lukkoye 10.1.2014, 19:58
Цитата(bsa @  9.1.2014,  10:52 Найти цитируемый пост)
И что, проблем не будет при подключении хидера в разных единицах трансляции и инстанцировании в них с одинаковыми параметрами шаблона?


Нет, не будет.

Кстати, это свойство шаблонов используют для проворачивания трюка "объявить и туловище и реализацию свободной функции в хэдэре так, что бы не иметь проблем с линкером".

Хотя лично я, для проворачивания аналогичного трюка использую статический метод обычного класса.
Если объявить и определить статический метод прямо в декларации класса, то проблем с линкером так же не будет.


Автор: mabrarov 10.1.2014, 20:37
Цитата(akizelokro @ 6.1.2014,  20:37)
Есть. Я даже подноготную рассмотрел 6-летней давности для применения typename и class. Начиная от исторических ссылок, вариантов применения в различных компляторов и тра-ля-ля ещё что-то там. Вообще сам typename и характер его применения сформировался именно на подходе к С++ 11. Я даже с примерами рассмотрел (я не тот чингачгук, который два раза на одни грабли наступает).

Коллеги, не "ткнете" ли меня (как автора первого ответа) туда, где сказано, что typename в данном случае имеет какое-то отношение к C++11. Думаю, разница м/у typename и class при использовании в шаблонах есть только в http://software.intel.com/ru-ru/blogs/2012/01/03/typename-class-2.

Цитата(akizelokro @ 6.1.2014,  20:37)
Вообще, я так понял, что вообще желательно typename пихать в код куда надо и, куда, как, казалось бы, не надо. И маложелательно употреблять class (вроде новомодная тенденция? )

Там, где я подразумеваю тип (например, в шаблонах), я пишу typename, а не class. Так мне понятнее и логичнее (тип же не ограничивается классом). Тенденция, думается, в лучших традициях C++03 smile

Добавлено через 8 минут и 15 секунд
Цитата(Lukkoye @ 10.1.2014,  19:58)
Кстати, это свойство шаблонов используют для проворачивания трюка "объявить и туловище и реализацию свободной функции в хэдэре так, что бы не иметь проблем с линкером".
Хотя лично я, для проворачивания аналогичного трюка использую статический метод обычного класса.
Если объявить и определить статический метод прямо в декларации класса, то проблем с линкером так же не будет.

Не проще ли inline-функции? Хотя в их случае компилятор волен http://habrahabr.ru/company/abbyy/blog/108166/ (а он может зависеть от препроцессора), что как бы намекает на дополнительную аккуратность при использовании inline-функций...

Автор: baldina 10.1.2014, 22:02
Цитата(mabrarov @  10.1.2014,  20:37 Найти цитируемый пост)
Коллеги, не "ткнете" ли меня (как автора первого ответа) туда, где сказано, что typename в данном случае имеет какое-то отношение к C++11

не-а. потому что это не так.

Автор: Lukkoye 11.1.2014, 03:18
Цитата(mabrarov @  10.1.2014,  20:37 Найти цитируемый пост)
Не проще ли inline-функции? Хотя в их случае компилятор волен выбрать любой вариант (а он может зависеть от препроцессора), что как бы намекает на дополнительную аккуратность при использовании inline-функций... 


Нет, не проще. Потому что inline или нет - решает компилятор. 

Необходимость следить за этим вручную делает этот подход несопоставимо более сложным, чем простая и узнаваемая всеми программистами статическая функция член, которая не отягощает интеллект дополнительными думками ещё и о линкере.

Автор: nonaot 14.1.2014, 22:53
Модератор: Сообщение скрыто.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)