Nested dependent names are not evaluated in template for concept

292 views Asked by At

(Originally separated from this question.)

In the following code snippet,

#include <concepts>

template<
    typename T,
    typename value_type = typename T::value_type
>
concept check_type = std::default_initializable<T>;

struct Foo {};

template<check_type T>
void func(T t) {}

int main()
{
    Foo foo;
    func(foo);
}

struct Foo does not contain the type alias value_type but it compiles without an error with GCC.

See the result tested on the compiler explorer.

However, with Clang, it reports the following error message:

❯ clang++ -std=c++20 asdf.cpp
asdf.cpp:17:5: error: no matching function for call to 'func'
    func(foo);
    ^~~~
asdf.cpp:12:6: note: candidate template ignored: constraints not satisfied [with T = Foo]
void func(T t) {}
     ^
asdf.cpp:5:39: note: because substituted constraint expression is ill-formed: no type named 'value_type' in 'Foo'
    typename value_type = typename T::value_type
                                      ^
1 error generated.

Also see the result tested on the compiler explorer.

Is this a bug?

1

There are 1 answers

2
T.C. On BEST ANSWER

GCC is correct under the current wording.

Per [temp.deduct]/5, satisfaction checking is done on the associated constraints of the function template:

If the function template has associated constraints ([temp.constr.decl]), those constraints are checked for satisfaction ([temp.constr.constr]).

[temp.constr.decl]/3.2 specifies that the associated constraint is based on the normal form:

A declaration's associated constraints are defined as follows:

  • ...
  • Otherwise, if there is a single introduced constraint-expression, the associated constraints are the normal form of that expression.

Since check_type is a concept, it is transparent to normalization ([temp.constr.normal]/1.4), and since the second template parameter of check_type is not used in its definition, that parameter does not appear in the normal form of the constraint expression. Therefore, the validity (or lack thereof) of T::value_type has no effect on satisfaction checking.

If you want the concept to check for value_type, it is more expressive (not to mention correct) to just check for value_type directly:

template <typename T>
concept check_type = std::default_initializable<T> && requires { typename T::value_type; };