10

Consider my simple example below:

#include <iostream>

template <typename T>
class Base
{
public:
    static constexpr int y = T::x;
};

class Derived : public Base<Derived>
{
public:
    static constexpr int x = 5;
};


int main()
{
    std::cout << Derived::y << std::endl;
}

In g++, this compiles fine and prints 5 as expected. In Clang, however, it fails to compile with the error no member named 'x' in 'Derived'. As far as I can tell this is correct code. Is there something wrong with what I am doing, and if not, is there a way to have this work in Clang?

  • Very similar question: http://stackoverflow.com/q/35759047/996886 – melak47 Mar 07 '16 at 22:18
  • If you initialize `y` outside of the `Base` class does it work? – Fantastic Mr Fox Mar 07 '16 at 22:18
  • @Ben you cannot intialize a `constexpr static int` outside the class. – melak47 Mar 07 '16 at 22:19
  • You can do what you usually have to do when you want to use typedefs etc. from the incomplete derived class, only refer to those in a member function's body: [example](http://coliru.stacked-crooked.com/a/258566e73ce6108e). The problem is, despite `y` being `constexpr`, you cannot use it anywhere in `Base` where it would be required when the template is instantiated - even [GCC agrees](http://coliru.stacked-crooked.com/a/7175bed7bdc5d6c1). I'm not sure GCC allowing your code, where the value is only used at runtime is correct, but I have no proof either way ;) – melak47 Mar 07 '16 at 22:33
  • 1
    Possible dup http://stackoverflow.com/questions/35753956/crtp-compiling-error/35754443#35754443 – StoryTeller - Unslander Monica Mar 08 '16 at 15:06
  • I have a similar question, although I'm not sure if it is a duplicate or not. I have solved my problem by defining my constant to be `const`, not `constexpr`, and initializing it outside the class body. http://stackoverflow.com/questions/38680819/error-when-declaring-static-constexpr-instance-of-derived-class-in-crtp-base-cla - I still want to get to the bottom of why mine (and possibly yours?) breaks, though. I'm not 100% convinced they're the same problem, either. – Merlyn Morgan-Graham Jul 31 '16 at 05:08
  • 1
    Also, see this question - http://stackoverflow.com/questions/37816186/initializing-a-static-constexpr-data-member-of-the-base-class-by-using-a-static - they say that the error you are seeing is standards conformant, and if you really want to do this, you need to get the constant via a function. – Merlyn Morgan-Graham Jul 31 '16 at 05:18

2 Answers2

2

As linked in the comments, Initializing a static constexpr data member of the base class by using a static constexpr data member of the derived class suggests that clang behaviour is standard conformant up to C++14 here. Starting with Clang 3.9, your code compiles successfully with -std=c++1z. An easy possible workaround is to use constexpr functions instead of values:

#include <iostream>

template <typename T>
class Base
{
public:
    static constexpr int y() {return T::x();}
};

class Derived : public Base<Derived>
{
public:
    static constexpr int x() {return 5;}
};

int main()
{
    std::cout << Derived::y() << std::endl;
}
user1531083
  • 750
  • 6
  • 15
1

This probably isn't the answer anyone would be looking for, but I solved the problem by adding a third class:

#include <iostream>

template <typename T>
class Base
{
public:
    static constexpr int y = T::x;
};

class Data
{
public:
     static constexpr int x = 5;
};

class Derived : public Base<Data>, public Data {};

int main()
{
    std::cout << Derived::y << std::endl;
}

It works as desired, but unfortunately it doesn't really have the benefits of CRTP!