Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clang and Intel fail to compile this CRTP code

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 ?

like image 271
Vincent Avatar asked Jan 08 '13 14:01

Vincent


2 Answers

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 ::).

like image 62
Marc Glisse Avatar answered Nov 14 '22 12:11

Marc Glisse


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.

like image 6
TemplateRex Avatar answered Nov 14 '22 11:11

TemplateRex