Внутренний класс в зависимости от аргумента шаблона

Рассмотрим следующий пример:

#include <iostream>
#include <typeinfo>

template< int N, typename T >
struct B
{
    struct C;
};

template< typename T >
struct B< 0, T >::C
{
    typedef T type;
};

template< int N, typename T >
struct B< N, T >::C
{
    typedef T type[N];
};

int main()
{
    std::cout<<"n=0   type = " << typeid( B< 0, float >::C::type ).name() << std::endl;
    std::cout<<"n=5   type = " << typeid( B< 5, float >::C::type ).name() << std::endl;
}

При компиляции с использованием g++ (версия 4.3.0)

g++ dfg.cpp  -ansi -pedantic -Wall

ошибка компиляции:

dfg.cpp:13: error: qualified name does not name a class before ‘{’ token
dfg.cpp: In instantiation of ‘B<0, float>::C’:
dfg.cpp:25:   instantiated from here
dfg.cpp:20: error: ISO C++ forbids zero-size array

Что я действительно пытаюсь заархивировать, так это иметь различную реализацию Imp в зависимости от значения перечисления (в примере вместо перечисления я использовал int, но это не должно иметь значения).

Кто-нибудь может объяснить, почему это запрещено? Почему я получаю первую ошибку? (это: полное имя не называет класс перед токеном «{»)


Что касается реализации pimpl в зависимости от параметра шаблона, я создал новый вопрос (с лучшим примером) здесь


person BЈовић    schedule 24.03.2011    source источник


Ответы (2)


Вы не можете определить C вне B таким образом — C не существует для специализации B, которую вы создаете. Если вы хотите специализироваться на B::C, вам нужно специализироваться на B. Вы пытаетесь сделать следующее?

template< int N, typename T >
struct B
{
    struct C {
        typedef T type[N];
    };
};

template< typename T >
struct B< 0, T >
{
    struct C {
        typedef T type;
    };
};

В качестве альтернативы вы можете сделать что-то вроде:

template< int N, typename T >
struct B
{
    struct C;
};

template< typename T >
struct B< 0, T > {
    struct C;
};

template< typename T >
struct B< 0, T >::C
{
    typedef T type;
};

template< int N, typename T >
struct B< N, T >::C
{
    typedef T type[N];
};

Это частично выделяет B для 0, и вперед объявляет C, так что можно определить B<0, T>::C.

person Erik    schedule 24.03.2011
comment
Не совсем. Я использую идиому pimpl и пытаюсь иметь разные ее реализации в зависимости от параметра шаблона. Вы можете предварительно объявить структуру C в структуре B и определить ее снаружи, но оба должны быть определены в момент создания экземпляра. - person BЈовић; 24.03.2011
comment
VJo: В вашем посте нет pImpl... То, что я написал, дает вам n=0 type=float и n=5 type=float [5] - чего вы пытаетесь достичь? - person Erik; 24.03.2011
comment
Структура C эмулирует идиому pimpl, потому что она предварительно объявлена ​​в структуре B. Итак, я должен объявить специализацию шаблона для структуры B, чтобы это работало? Почему он не может использовать объявление B, которое там есть? - person BЈовић; 24.03.2011
comment
@VJo: ничто не мешает вам, например. создание специализации B, у которой вообще нет struct C. Таким образом, компилятор не может знать, что специализация B имеет член C, пока вы не сообщите ему об этом, показав ему специализацию. - person Erik; 24.03.2011
comment
Хорошо, я создам новый вопрос - person BЈовић; 24.03.2011

Это недействительно:

template< typename T >
struct B< 0, T >::C
{
    typedef T type;
};

Вы можете специализировать элементы шаблонов классов, но только для неявных экземпляров этих шаблонов классов. На простом английском языке это означает: Только если вы дадите значения для всех аргументов шаблона включающего шаблона класса (член которого вам нужно специализировать).

template< >
struct B< 0, int >::C
{
    typedef T type;
};

То, что вы написали, является определением B<0, T>::C, который является членом частичной специализации шаблона класса B<N, T>. Такой частичной специализации не существует, поэтому компилятор выдал ошибку.


У вас есть несколько вариантов решения этой проблемы. Один

template< int N, typename T >
struct B
{
    template<typename N1, typename T1>
    struct CMember { typedef T1 type[N1]; };

    template<typename T1>
    struct CMember<0, T1> { typedef T1 type; };

    struct C { 
      typedef typename CMember<N, T>::type type;
    };
};

Обратите внимание, что явные специализации (не частичные) не могут быть помещены в шаблон класса напрямую (поэтому template<> struct CMember<0, int> { ... } будет неправильно сформировано при записи внутри тела B). Затем вам нужно будет определить шаблон "селектор" вне B (возможно, в пространстве имен detail).

Другие альтернативы включают производные от CMember и наследование его typedefs.

person Johannes Schaub - litb    schedule 24.03.2011