11

I have written a small library that use a lot of C++11 metaprogramming techniques and CRTP, and it compiles well with g++ 4.7.2

Now, I try to compile it with Intel icpc 13.0.0.079 and it generates several hundreds of errors. So I try to isolate the problems one after another.

So, first, consider this code, that compiles with no problem under g++ 4.7.2

#include <iostream>

template<template<typename> class Crtp, typename Type>
struct Base {};

template<typename Type>
struct Derived : public Base<Derived, Type>
{
    Derived(): Base<Derived, Type>() {;}
};

int main()
{
    Derived<int> x;
    return 0;
}

Both icpc and clang fail to compile this code:

test_crtp.cpp(26): error: type "Derived<Type>::Derived" is not a class template
      Derived(): Base<Derived, Type>() {;}
                      ^

test_crtp.cpp(26): error: "Base" is not a nonstatic data member or base class of class "Derived<int>"
      Derived(): Base<Derived, Type>() {;}
                 ^
          detected during instantiation of "Derived<Type>::Derived() [with Type=int]" at line 31

compilation aborted for test_crtp.cpp (code 2)

So is it a bug in intel and clang, or in g++ ? If it's in intel and clang, do you think that it will be solved in a future version ?

Vincent
  • 57,703
  • 61
  • 205
  • 388
  • It's a good question, but usually CRTP doesn't bother with a template-template parameter, but just has the Derived class pass the type. `template struct Base; template struct Derived: Base< Derived > { ... };` is more normal. – Dave S Jan 08 '13 at 14:35
  • ...if you wanted to propagate `Type`, may be you can have a `typedef` in `Derived` which exposes it... – Nim Jan 08 '13 at 14:38
  • 1
    The identifier `Derived` names, inside its own class, the complete type `Derived`. This is called an *injected* class name. I think this is a bug with GCC. – Xeo Jan 08 '13 at 14:41
  • + for good question, I want see a definitive answer too –  Jan 08 '13 at 14:47
  • @Xeo : and is there a way to get the non-complete type inside the class ? – Vincent Jan 08 '13 at 14:49
  • Only through fully qualifying it like Marc says. – Xeo Jan 08 '13 at 14:49
  • @Dave S and @Nim: you cannot always have a nested typedef inside Derived. E.g. `Derived` is not a complete type yet when you try to use it inside `Base`. Vincent's use of a template-template parameter is required if you need the inner typedef already in the Base. – TemplateRex Jan 08 '13 at 15:58

2 Answers2

7

Inside the class Derived, the name Derived refers to the (instantiated) class, not the class template. Try Base< ::Derived, Type> instead (be careful to leave a space between < and ::).

Marc Glisse
  • 7,550
  • 2
  • 30
  • 53
6

In section 9.2.3 of C++ Template the Complete Guide (Amazon), there is a discussion on Injected Class Names. To quote:

Class templates also have injected class names. However, they're stranger than ordinary injected class names: They can be followed by template arguments (in which case they are injected class template names), but if they are not followed by template arguments they represent the class with its parameters as its arguments (or, for a partial specialization, its specialization arguments). This explains the following situation:

template<template<typename> class TT> 
class X {};

template<typename T> 
class C 
{
    Ca;        // OK: same as ''C<T> a;''
    C<void> b; // OK
    X<C> c;    // ERROR: C without a template argument list
               // does not denote a template
    X<::C> d;  // ERROR: <: is an alternative token for [
    X< ::C> e; // OK: the space between < and :: is required
}

Note how the unqualified name refers to the injected name and is not considered the name of the template if it is not followed by a list of template arguments. To compensate, we can force the name of the template to be found by using the file scope qualifier ::. This works, but we must then be careful not to create a so-called digraph token <:, which is interpreted as a left bracket. Although relatively rare, such errors result in perplexing diagnostics.

So what happens in your code is that Base<Derived, Type> is being interpreted as Base<Derived<Type>, Type> which is ill-formed. Therefore you need to use the scope qualifier :: with a space between the < to avoid a digraph.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304