c++language-lawyerc++20requires-clause

`requires` expression is evaluated to false in a nested template, but code is still compiled


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;
}

Solution

  • 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.