16

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;
}
康桓瑋
  • 33,481
  • 5
  • 40
  • 90
Jean Amaro
  • 315
  • 1
  • 7
  • 1
    Have you tried whether other compilers agree with msvc? (Godbolt compiler explorer is great) – Marcus Müller Aug 25 '22 at 23:01
  • 1
    Unrelated: if the code does not compile with `-std=c++20` your GCC install's not quite as latest as you think – user4581301 Aug 25 '22 at 23:12
  • `clang++` refuses it – Ted Lyngmo Aug 25 '22 at 23:24
  • 1
    @TedLyngmo But seemingly for the wrong reason. I don't think there is anything wrong with having the `requires`-clause where Clang complains about it. But compilation should fail because `Wrapper < int >` does not satisfy the constraints of `Wrapper` (at least intuitively). – user17732522 Aug 25 '22 at 23:34
  • @user17732522 I must admit I have very little xp when it comes to concepts in general and have a hard time wrapping my head around the requires thing in the question :) – Ted Lyngmo Aug 25 '22 at 23:43
  • @TedLyngmo It is just a template template parameter which has a requires-clause like any template could have in that position. Of course `requires(false)` specifically doesn't make much sense (and maybe is even IFNDR, don't know), but imagine it was `template < typename T > requires std::same_as typename Wrapper` instead. – user17732522 Aug 25 '22 at 23:47
  • This would basically mean that this template template parameter should be passed a template template argument that would accept `float` as a template argument. But then intiuitively it seems that `Wrapper < int >` should fail, not because the actual template template argument doesn't accept `int`, but because the template template parameter asks only for a template template argument which accepts `float`, not `int`. – user17732522 Aug 25 '22 at 23:47
  • A bit of a side note, I believe the flag `-std=c++2a` is the temporary flag compilers use to allow you to opt-in to the WIP features for C++20 (similar to `-std=c++2b` for C++23), with complete features being activated with `-std=c++20` when available. Looking at cppreference's [C++20 compiler support page](https://en.cppreference.com/w/cpp/compiler_support/20), it appears that Concepts are only complete in GCC 10, Clang 10, and only partially supported by MSVC 19.23-19.30. So it could be the case that whatever compiler version you're using doesn't fully support concepts yet. – oraqlle Aug 26 '22 at 01:20

1 Answers1

7

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.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • Somewhat similar question: https://stackoverflow.com/questions/68622369/why-is-the-concept-in-template-template-argument-not-verified – Fedor Aug 28 '22 at 10:19