views:

111

answers:

4

I have some template code which compiles fine in VC9 (Microsoft Visual C++ 2008) but won't compile in GCC 4.2 (on Mac). I'm wondering if there's some syntactical magic that I'm missing.

Below I have a stripped-down example which demonstrates my error. Sorry if this example seems meaningless, I removed as much as I could to isolate this error.

In particular I have a template class S which has an inner class R which is also a template class. From a top-level template function foo, I am trying to call R::append which is a static member function of R:

template< typename C >
struct S {
    template< typename T >
    S<C> & append( const T & ) { return *this; }

    template< int B >
    struct R {
        template< typename N >
        static S<C> & append( S<C> & s, const N ) {
            return s.append( 42 );
        }
    };
};

template< typename C >
S<C> & foo( S<C> & s, const int n ) {
    S<C>::R<16>::append( s, n ); // error: '::append' has not been declared
    return s;
}

Anyone out there know what I'm doing wrong?

A: 

Try writing "const int N" in struct R::append and then use N (rather than 42?).

+4  A: 

I get it to compile with:

template< typename C >
S<C> & foo( S<C> & s, const int n ) {
    typedef typename S<C>::template R<16> SR;
    SR::append( s, n );
    return s;
}
Magnus Hoff
The typedef is not necessary, but I too prefer to alias complex types even if I only use them once, just for clarity's sake.
Matthieu M.
+4  A: 

You have to tell the compiler that the dependent name R is a template:

template< typename C >
S<C> & foo( S<C> & s, const int n ) {
    S<C>::template R<16>::append( s, n );
    return s;
}
sth
So you think VC is non-conformant by accepting the original code?
Terry Mahaffey
Thanks for the help. This indeed fixes my errors on the Mac. I fixed my actual project as well and it builds now on both Mac and PC. I'm not sure which compiler is more conformant.
jwfearn
@Terry: I posted an answer to actually explain the thing. I think gcc is actually more conformant on that point, but I may be wrong.
Matthieu M.
+2  A: 

Having use both Visual Studio and gcc, it's a known issue :) And I used VS2003 and gcc 3.4.2 so it's been like that for a while.

If I remember correctly, the problem is due to the way templates are parsed on these compilers.

gcc behaves as stated by the standard, and performs 2 parses:

  • the first when encountering the template, without any information on the types, at this point it necessitates some typename and template magic to help understand what's going on
  • a second when actually instantiating the template with a given type

on the other hand, VS only does one parse, at instantiation, and therefore can fully resolve the symbols without typename and template here and there.

You have the same thing for methods:

template <class Item>
struct Test
{
  template <class Predicate>
  void apply(Predicate pred);

  void doSomething { this->apply(MyPredicate()); }          // Visual Studio
  void doSomething { this->template apply(MyPredicate()); } // gcc
}; // struct Test

On the same topic, if you do something like:

template <class Item>
struct Test { static const std::string Name; };

You need to actually define this static attribute for each instantiation of the template, or you'll have an undefined symbol.

VS accepts this syntax:

const std::string Test<MyType>::Name = "MyType";

But gcc asks for a little keyword:

template <> const std::string Test<MyType>::Name = "MyType";

You may think of VS as better since it asks less of you, but on the other hand gcc may warn you of mistakes in your template methods / classes as soon as it parses them the first time (ie without any actual instantiation) and personally, the sooner the better.

Obviously, the good news is that if compiles on gcc (for these issues), it will also compile fine on Visual Studio.

Since I am not a standardista though, I am not sure whether or not the standard actually demands or advises the 2-parses scheme.

Matthieu M.
The standard does demand two-phase lookup for non-dependent and dependent names.
ephemient