Search code examples
c++g++c++20clang++template-specialization

Clang partial class template specialization error


I've the following simple c++20 test:

#include <type_traits>
/////////////////////////////////////// constraints

template <typename Type> concept isConst = ::std::is_const_v<Type>;

template <typename Type> concept isNotConst = !isConst<Type>;

/////////////////////////////////////// class decleration

template <typename T>
class TestRef;

template <isNotConst T>
struct TestRef<T> {
    explicit TestRef(T& value) noexcept;
};

template <isConst T>
struct TestRef<T> {
    explicit TestRef(T& value) noexcept;
};

/////////////////////////////////////// class implementation

template <isNotConst T>
TestRef<T>::TestRef(T& value) noexcept {}

template <isConst T>
TestRef<T>::TestRef(T& value) noexcept {}

/////////////////////////////////////// main

int main()
{
    int a{ 1 };
    TestRef<int> ref1{ a };
    TestRef<const int> ref2{ a };
    return 0;
}

This exemple compiles fine with g++ 12.2.0. However, using clang++ 14.0.6, it does not. Here is the error:

error: incomplete type 'TestRef' named in nested name specifier

I failed to identify the reason why it does and how to work around it.

What I understood is that it does not choose the specialized structure. It uses the basic one that is not defined.

If I do define it, I get the error:

error: type constraint differs in template redeclaration

What can I do to fix the issue?


Solution

  • If there's no reason that you must use concepts, you can use a pre-C++20 way to implement it.

    template <typename T, typename = void>
    class TestRef;
    
    template <typename T>
    struct TestRef<T, std::enable_if_t<std::is_const_v<T>>> {
        explicit TestRef(T& value) noexcept;
    };
    
    template <typename T>
    struct TestRef<T, std::enable_if_t<!std::is_const_v<T>>> {
        explicit TestRef(T& value) noexcept;
    };
    
    /////////////////////////////////////// class implementation
    
    template <typename T>
    TestRef<T, std::enable_if_t<std::is_const_v<T>>>::TestRef(T& value) noexcept {}
    
    template <typename T>
    TestRef<T, std::enable_if_t<!std::is_const_v<T>>>::TestRef(T& value) noexcept {}
    

    Demo

    I'm showing you a general way. In your case, you don't have another specialization for the non-const version. You can fill them in the primary template.