2

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

Community
  • 1
  • 1
BЈовић
  • 62,405
  • 41
  • 173
  • 273

2 Answers2

3

This is not valid:

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

You can specialize members of class templates, but only for implicit instantiations of those class templates. This means in plain English: Only if you give values for all the template arguments of the enclosing class template (whose member you need to specialize).

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

What you wrote is the definition of a B<0, T>::C, which is a member of a class template partial specialization of B<N, T>. Such a partial specialization does not exist, therefor, the compiler errored out.


You have several options to solve this. One is

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

Note that explicit specializations (non-partial) cannot be put into the class template directly (so, template<> struct CMember<0, int> { ... } would be ill-formed when writting inside the body of B). You would need to define the "selector" template outside B then (perhaps in a detail namespace).

Other alternatives include deriving from CMember and inheriting its typedefs.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
2

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.

Erik
  • 88,732
  • 13
  • 198
  • 189
  • Not really. I am using the pimpl idiom, and trying to have different implementations of it depending on the template parameter. You can forward declare the struct C in struct B, and define it outside, but both have to be defined at the moment of the instantiation – BЈовић Mar 24 '11 at 13:22
  • VJo: There's no pImpl in your post... What I posted gives you `n=0 type=float` and `n=5 type=float [5]` - what are you trying to achieve? – Erik Mar 24 '11 at 13:25
  • The struct C emulates the pimpl idiom, because it is forward declared in the struct B. So, I have to declare the template specialization for struct B in order to have this working? Why it can't use the declaration of B it is there? – BЈовић Mar 24 '11 at 13:53
  • @VJo: There's nothing preventing you from e.g. creating a B specialization that doesn't have a `struct C` at all. So the compiler can't know that the specialization of `B` has a `C` member unless you tell it, by showing it the specialization. – Erik Mar 24 '11 at 14:02
  • Right, I will create new question – BЈовић Mar 24 '11 at 19:48