Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

multiple nested dependent names - where to stick the typename keyword?

This question was inspired by this other question. While trying to answer that question, I understood that I have a lot of questions myself. So... Consider the following:

struct S1
{
    enum { value = 42 };
};

template <class T> struct S2
{
    typedef S1 Type;
};

template <class T> struct S3
{
    typedef S2<T> Type; 
};

template <class T> struct S4
{
    typedef typename T::Type::Type Type;  //(1)//legal?
    enum {value = T::Type::Type::value }; //(2)//legal?
};

int main()
{
    S4<S3<S2<S2<S1> > > >::value;
}

This compiles successfully with MSVC9.0 and Online Comeau. However, what's bothering me is that I don't understand what typename refers to in (1) and why wouldn't we need typename in (2).

I have tried these 2 syntaxes (syntaces?) of what I think it should be both of which fail on MSVC:

    typedef typename T::typename Type::Type Type;
    enum {value = typename T::typename Type::Type::value }; 

and

    typedef typename (typename T::Type)::Type Type;
    enum {value = (typename (typename T::Type)::Type)::value }; 

Of course, a workaround is to use consecutive typedefs like this:

   typedef typename T::Type T1;
   typedef typename T1::Type Type;
   enum { value = Type::value};  

Good style left aside, do we syntactically have to use the workaround I mentioned?

The rest is just an interesting example. No need to read. Not that relevant to the question.

Please note, that although MSVC accepts the original strange syntax without multiple typenames(I mean (1) and (2)), it leads to strange behavior as in the mentioned question. I think I'll present that example in concise form here as well:

struct Good
{
    enum {value = 1}; 
};
struct Bad
{
    enum {value = -1};  
};

template <class T1, class T2>
struct ArraySize
{
    typedef Bad Type;
};
template <class T>
struct ArraySize<T, T>
{
    typedef Good Type;
};

template <class T>
struct Boom
{
    char arr[ArraySize<T, Good>::Type::value]; //error, negative subscript, even without any instantiation
};

int main()
{
    Boom<Good> b; //with or without this line, compilation fails.
}

This doesn't compile. The workaround I mentioned solves the problem, but I am sure the problem here is my initial question - missing typename, but you don't really know where to stick one. Thanks very much in advance.

like image 990
Armen Tsirunyan Avatar asked Jul 10 '11 17:07

Armen Tsirunyan


Video Answer


1 Answers

The name before the scope operator :: must always be a namespace or class (or enumeration) name, and namespace names can't be dependent. So you don't have to tell the compiler that this is a class name.


I'm not just making this up, the standard says (section [temp.res]):

A qualified name used as the name in a mem-initializer-id, a base-specifier, or an elaborated-type-specifier is implicitly assumed to name a type, without the use of the typename keyword. In a nested-name-specifier that immediately contains a nested-name-specifier that depends on a template parameter, the identifier or simple-template-id is implicitly assumed to name a type, without the use of the typename keyword. [ Note: The typename keyword is not permitted by the syntax of these constructs. — end note ]

T::, T::Type::, and T::Type::Type:: are nested-name-specifiers, they do not need to be marked with typename.

This section clearly could have, and arguably should have, included the type-specifier of a typedef declaration in the list of exemptions. But remember that type-specifiers can get really complicated, especially in typedef declarations. Right now it's quite possible to need the typename keyword multiple times in a typedef type-specifier, so a lot more analysis would be needed to convince me that typename is never necessary in a typedef.

In typedef typename T::Type::Type Type, T::Type::Type requires use of the typename keyword, because its nested-name-specifier (T::Type::) is a dependent name, and the standard says (same section):

When a qualified-id is intended to refer to a type that is not a member of the current instantiation (14.6.2.1) and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier. If the qualified-id in a typename-specifier does not denote a type, the program is ill-formed.

like image 84
Ben Voigt Avatar answered Sep 17 '22 12:09

Ben Voigt