Question

Behavior of concepts with a default template parameter

Consider this concept, which has a default template parameter.

template<class T, class = decltype([]{})>
concept IsDefined = sizeof(T) > 0;

Since every lambda has a distinct type, one might expect every instantiation of IsDefined<X> to be distinct.

struct SomeType;

static_assert( false == IsDefined<SomeType> );

struct SomeType
{
   // Defined.
};

static_assert( true == IsDefined<SomeType> );

Clang and MSVC agree and compile this code. GCC fails the second static assert, but only if the first static assert is evaluated.

error: static assertion failed
   17 | static_assert( true == IsDefined<SomeType> );

Are any of these compilers wrong? Or is this behavior unspecified?

See compiler results here

 3  136  3
1 Jan 1970

Solution

 9

Concepts are not instantiated. Instead, the expression is normalized into a constraint, and then when a concept-id is evaluated, the constraint is checked for satisfaction.

In this case the normal form of IsDefined is the atomic constraint sizeof(T) > 0 with the identity mapping T -> T; note that the second parameter does not appear in the mapping (see [temp.constr.atomic]/1). Your code is IFNDR by [temp.constr.atomic]/3:

If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required.

2024-07-20
T.C.