What are the rules for out-of-line definitions of constrained member templates?

Viewed 209

Consider the following code:

template <typename T>
struct S 
{
    template <typename = void>
    static constexpr bool B = true;

    template <std::enable_if_t<S<T>::template B<>, int> = 0>
    void f();
};

template <typename T>
template <std::enable_if_t<S<T>::template B<>, int>>
void S<T>::f() {}

gcc accepts this, but clang rejects it with:

error: out-of-line definition of 'f' does not match any declaration in 'S<T>'

This has been asked about before, but there is no answer there.


On the other hand, if B is not a template, and I write this code:

template <typename T>
struct S 
{
    static constexpr bool B = true;

    template <std::enable_if_t<S<T>::B, int> = 0>
    void f();
};

template <typename T>
template <std::enable_if_t<S<T>::B, int>>
void S<T>::f() {}

clang accepts this, but gcc rejects the code with:

error: no declaration matches 'void S<T>::f()'

So are either of these snippets valid?

1 Answers

During the definition of S<X> it is an incomplete type. And the class member access operator requires a complete type.

But you could solve the situation with the following code:

#include <type_traits>

template <typename T>
struct S {

    template <typename = void>
    static constexpr bool B = true;

    template <
        typename TX = T,
        std::enable_if_t<S<TX>::template B<>, int> = 0>
    void f();
};

template <typename T>
template <typename TX, std::enable_if_t<S<TX>::template B<>, int>>
void S<T>::f() {}

//-----------

template <typename T>
struct S2 {

    static constexpr bool B = true;

    template <
        typename TX = T,
        std::enable_if_t<S2<TX>::B, int> = 0>
    void f();
};

template <typename T>
template <typename TX, std::enable_if_t<S2<TX>::B, int>>
void S2<T>::f() {}
Related