Search code examples
c++visual-c++metaprogrammingcrtpstatic-polymorphism

c++ static polymorphism (CRTP) Resulting in incomplete type when evaluating a `static constexpr`


I need access to a static constexpr and one solution I put together works with gcc (live example) but not with vc++ (live example).

The code is as follows:

template<class Drvd>
class Base
{
public:
    static constexpr bool val = Drvd::val;
};

class Derived : public Base<Derived>
{
    friend class Base;
private:
    static constexpr bool val = true;
};

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

So it is a bug with vc++, but anyone has an idea on how to achieve val defined in Base as the value of val in Drvd in a different way that vc++ won't complain about?

Edit: Note that the result is the same with the variant: friend class Base<Derived>; instead of friend class Base;


Solution

  • Per @David, the problem has to do with Face being incomplete, because it goes into Base before finishing the definition of Face.

    However, the solution linked to by @David is a bit old and misses some tricks of which we can take advantage. Namely, @m.s. shows us that static constexpr functions are fine - and also based on my own experimentation - and we really only need to deal with this particular case of static constexpr variables, and perhaps types accessed from Derived.

    The following (live example) shows how to solve this problem, while keeping each class encapsulated to it's own h-file, making it somewhat cleaner:

    #include <iostream>
    
    // Begin: h-file of Base
    template<class Drvd>
    class ConstValue;
    
    template<class Drvd>
    class Base
    {
    public:
        static constexpr bool val = ConstValue<Drvd>::val;
    };
    // End: h-file of Base
    
    // Begin: h-file of Derived
    class Derived;
    
    template<>
    class ConstValue<Derived>
    {
    public:
        static constexpr bool val = true;
    };
    
    class Derived : public Base<Derived>
    {
        friend class Base<Derived>;
    private:
        static constexpr bool val = true; // optional
    };
    // End: h-file of Derived
    
    // Main
    int main()
    {
        std::cout << Derived::Base::val << std::endl;
    }
    

    The general idea is that for each constexpr that Base needs to access from Derived, we can create a single class that encapsulate the variables, and then overload the class for each Derived that uses Base.