Search code examples
c++language-lawyerc++20c++-concepts

Concept requirement and non-immediate context


I'm learning C++ concepts, and trying to realize why the following does not compile (it's just a trivial and meaningless example demonstrating the point; tested with GCC-11.1):

#include <iostream>

struct non_negatable {
};

template <class T>
auto negate(T a) {
    return -a;
}

template <class T>
concept is_negatable = requires(T t) {
    //just `-a;` would do the job of course
    negate(t);
};

template <class T>
auto do_negate(T) {
    std::cout << "here we are\n";
}

template <is_negatable T>
auto do_negate(T t) {
    return negate(t);
}

int main() {
    non_negatable x;
    do_negate(x);
}

The above concept attempts to call a function template, whose implementation would not compile. This fact causes a "hard error", rather than failing the concept.

I guess it works this way because the concept requirements may be failed only by the expressions' "immediate context" ill-formness (like what we could observe with SFINAE).

Is my understanding correct? Where does the Standard describe this point? Is there a way to "fail" a concept based on a function template implementation ill-formness?


Solution

  • Firstly, as you referred, negate<non_negatable> is not a substitution failure since the error is not in the immediate context of the function, as such it's ill-formed. From 13.10.3.1/8 of C++20 standard:

    If a substitution results in an invalid type or expression, type deduction fails.

    An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments.

    [Note 4: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. — end note]

    Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.

    [Note 5: The substitution into types and expressions can result in effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such effects are not in the “immediate context” and can result in the program being ill-formed. — end note]

    Secondly, 7.5.7.1/6 says:

    [...]

    [Note 1: If a requires-expression contains invalid types or expressions in its requirements, and it does not appear within the declaration of a templated entity, then the program is ill-formed. — end note]

    [...]

    So, your understanding seems correct to me. However, what you're asking in your last question boils down to whether there is a way to make an invalid expression in a template function body cause a substitution failure instead of an ill-formed program. I don't think it's possible.