Here is a struct with a templated constructor that is defined out of line:
template <typename T>
struct Foo {
template <typename F>
Foo(F f);
};
template <typename T>
template <typename F>
Foo<T>::Foo(F f) {}
Clang is happy with this under -std=c 20
. If I add a requires
clause to the templated constructor, it is still happy. But if the requires
clause mentions the struct, it is not happy:
#include <concepts>
template <typename T>
struct Foo {
template <typename F>
requires (!std::same_as<Foo<T>, int>)
Foo(F f);
};
template <typename T>
template <typename F>
requires (!std::same_as<Foo<T>, int>)
Foo<T>::Foo(F f) {}
<source>:13:9: error: out-of-line definition of 'Foo<T>' does not match any declaration in 'Foo<T>'
Foo<T>::Foo(F f) {}
^~~
GCC does accept this.
Is clang right to reject it? And if so, what part of the standard says so?
If you're interested why I have a requires
clause that references the type being constructed, it's to disambiguate the constructor from the move constructor so that the next requires
clause in a larger requires
expression won't be evaluated when F
is the same as Foo
. Otherwise it recursively depends upon itself. The real code is more complicated, and accepts a forwarding reference to F
.
CodePudding user response:
I think this could be related to the compiler not being able to deduce what T is. When using a requires clause inside a struct, the compiler needs to be able to instantiate the class template in order to check the requirements specified in the requires clause. This means that all template parameters used within the requires clause must be in scope and the compiler must be able to deduce their types. If the class template is not fully defined yet, the compiler will not be able to instantiate it, and the code will not be able to compile. Just to be clear, I do not know with certainty, as I have barely used this feature.
CodePudding user response:
As I sort of expected, this seems to be a known clang bug: https://github.com/llvm/llvm-project/issues/49620