I am failing to understand how the requires
keyword works inside a nested template.
The code below can be compiled on the latest versions of MSVC and gcc (using /std:c latest
and -std=c 2a
, respectively).
Is the requires
simply discarded in scenarios like this? Should I not use it this way?
#include <type_traits>
template <
template < typename >
requires (false) // Should not this stop compilation?
typename Wrapper >
using Test = Wrapper < int >;
template < typename >
struct S
{
};
int main() {
Test < S > var;
return 0;
}
CodePudding user response:
I think the compilers are not implementing this correctly and you are correct that it should fail to compile.
In [temp.names]/7 it says that a template-id formed from a template template parameter with constraints must satisfy these constraints if all template arguments are non-dependent.
You are giving Wrapper
only one argument, namely int
which is not dependent. Therefore the compiler should check whether Wrapper<int>
satisfies the constraint requires(false)
of Wrapper
. This check should fail.
I am not completely sure that requires(false)
specifically is not IFNDR, because there are some similar rules forbidding e.g. templates which can never be instantiated, but the compilers seem to behave the same way if a non-trivial constraint is used.
Clang complains that the requires
clause is a syntax error in that position, but I don't see any reason for that.
MSVC actually handles for example the following variation using a type constraint instead of a requires-clause as expected:
template<
template<std::same_as<float> T>
typename Wrapper>
using Test = Wrapper<int>;
but does not reject as expected if a requires-clause is used:
template<
template<typename T>
requires std::same_as<float, T>
typename Wrapper>
using Test = Wrapper<int>;
Interestingly Clang crashes with an ICE on the former.