|
| 1 | +//type:fp |
| 2 | +//options:--c++20:--c++20 --g++ --gnu_version=150200:--c++20 --clang --clang_version=210000:--ms_c++20 --microsoft_version=1950 |
| 3 | +//remark:decltype of a template parameter in a requires-expression parameter |
| 4 | + |
| 5 | +// The parameter declarations of a requires-expression appearing outside of a |
| 6 | +// concept definition did not record the information needed to substitute |
| 7 | +// decltype applied to a template parameter. The substituted parameter type |
| 8 | +// was therefore unusable, and any requirement using the parameter in an |
| 9 | +// operation was considered unsatisfied. In a member template of a class |
| 10 | +// template, decltype applied to a class-type template parameter of the |
| 11 | +// enclosing class template also lost its id-expression form when the class |
| 12 | +// template was instantiated, so the parameter got the const type of the |
| 13 | +// template parameter object. |
| 14 | + |
| 15 | +template <typename T, typename U> constexpr bool is_same = false; |
| 16 | +template <typename T> constexpr bool is_same<T, T> = true; |
| 17 | + |
| 18 | +struct NoConst { |
| 19 | + int operator()() { return 0; } |
| 20 | +}; |
| 21 | + |
| 22 | +// F is a const lvalue (a template parameter object), but decltype(F) is the |
| 23 | +// declared type of the template parameter, which is not const. |
| 24 | +template <auto F> |
| 25 | +inline constexpr bool const_callable_v = requires { F(); }; |
| 26 | +template <auto F> |
| 27 | +inline constexpr bool non_const_callable_v = |
| 28 | + requires(decltype(F) f) { f(); }; |
| 29 | +static_assert(!const_callable_v<NoConst{}>); |
| 30 | +static_assert(non_const_callable_v<NoConst{}>); |
| 31 | + |
| 32 | +// A template parameter declared with the class type. |
| 33 | +template <NoConst F> |
| 34 | +constexpr bool typed_v = requires(decltype(F) f) { f(); }; |
| 35 | +static_assert(typed_v<NoConst{}>); |
| 36 | + |
| 37 | +// The parameter has exactly the declared type. |
| 38 | +template <auto F> |
| 39 | +constexpr bool exact_type_v = |
| 40 | + requires(decltype(F) f) { requires is_same<decltype(f), NoConst>; }; |
| 41 | +static_assert(exact_type_v<NoConst{}>); |
| 42 | + |
| 43 | +// Declarators built on the decltype type. |
| 44 | +template <auto F> |
| 45 | +constexpr bool ref_v = requires(decltype(F) &f) { f(); }; |
| 46 | +static_assert(ref_v<NoConst{}>); |
| 47 | +template <auto F> |
| 48 | +constexpr bool ptr_v = requires(decltype(F) *f) { (*f)(); }; |
| 49 | +static_assert(ptr_v<NoConst{}>); |
| 50 | + |
| 51 | +// The parenthesized form denotes the const template parameter object. |
| 52 | +template <auto F> |
| 53 | +constexpr bool paren_v = requires(decltype((F)) f) { f(); }; |
| 54 | +static_assert(!paren_v<NoConst{}>); |
| 55 | + |
| 56 | +// A nontype template parameter of non-class type. |
| 57 | +template <auto N> |
| 58 | +constexpr bool int_v = requires(decltype(N) n) { n + 1; }; |
| 59 | +static_assert(int_v<42>); |
| 60 | + |
| 61 | +// A requires-expression in a function template body. |
| 62 | +template <auto F> constexpr bool in_function() { |
| 63 | + return requires(decltype(F) f) { f(); }; |
| 64 | +} |
| 65 | +static_assert(in_function<NoConst{}>()); |
| 66 | + |
| 67 | +// A requires-expression in a member of a class template. |
| 68 | +template <auto F> struct S { |
| 69 | + static constexpr bool value = requires(decltype(F) f) { f(); }; |
| 70 | + template <int I> static constexpr bool member_template() { |
| 71 | + return requires(decltype(F) f, decltype(I) i) { f() + i; }; |
| 72 | + } |
| 73 | + template <int I> static constexpr bool member_exact_type() { |
| 74 | + return requires(decltype(F) f) { |
| 75 | + requires is_same<decltype(f), NoConst>; |
| 76 | + }; |
| 77 | + } |
| 78 | + template <int I> static constexpr bool member_paren() { |
| 79 | + return requires(decltype((F)) f) { f(); }; |
| 80 | + } |
| 81 | + template <int I> static constexpr bool out_of_class(); |
| 82 | +}; |
| 83 | +template <auto F> template <int I> |
| 84 | +constexpr bool S<F>::out_of_class() { |
| 85 | + return requires(decltype(F) f) { f(); }; |
| 86 | +} |
| 87 | +static_assert(S<NoConst{}>::value); |
| 88 | +static_assert(S<NoConst{}>::member_template<1>()); |
| 89 | +static_assert(S<NoConst{}>::member_exact_type<1>()); |
| 90 | +static_assert(!S<NoConst{}>::member_paren<1>()); |
| 91 | +static_assert(S<NoConst{}>::out_of_class<1>()); |
| 92 | + |
| 93 | +// This already worked in a concept definition. |
| 94 | +template <auto F> |
| 95 | +concept NonConstCallable = requires(decltype(F) f) { f(); }; |
| 96 | +static_assert(NonConstCallable<NoConst{}>); |
0 commit comments