Search code examples
c++templatesmetaprogrammingpartial-specializationpimpl-idiom

Inner class depending on a template argument


Consider next example :

#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;
}

When compiled using g++ (version 4.3.0)

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

the compile error is :

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

What I am really trying to archive is to have different Imp implementation depending on the enum value (in the example, instead of an enum, I used int, but it shouldn't matter).

Can someone explain why is this not allowed? Why am I getting the first error? (this one : qualified name does not name a class before ‘{’ token)


Regarding the pimpl implementation depending on a template parameter, I created a new question (with better example) here


Solution

  • You can't define C outside B this way - C doesn't exist for the B specialization you're creating. If you want to specialize B::C, you need to specialize B. Are you trying to do the following?

    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;
        };
    };
    

    Alternatively, you can do something like:

    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];
    };
    

    This partially specializes B for 0 and forward declares C, so that B<0, T>::C can be defined.