On Mon, Jan 6, 2020 at 8:33 PM Arthur O'Dwyer via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
On Fri, Jan 3, 2020 at 3:46 PM Askar Safin via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
TL;DR: Require users to specify all necessary concepts in template. This is, of course, incompatible change, so it should be implemented using epochs ( http://wg21.link/P1881 ). This is what templates should be right from the beginning.

You seem to be talking about something basically the same as what used to be called "definition checking." The idea would be that if we had some way to specify that a type was "addable," then we could write

    template<Addable T> auto add(T x, T y) { return x + y; }

The problem with this in C++2a is that C++2a Concepts are "Concepts Lite"; they do not, and fundamentally cannot, support definition checking. Consider what would happen if you wrote the following C++2a code:

    template<class T>
    concept Addable = requires(const T& t) {
        { t + t };

Without definition checking, our "add" function template above compiles fine (because you don't have to do any checking on the template itself), and you can even instantiate it with common-law-addable types, so that we can form `add<int>` and `add<std::string>`.
But if we blindly added definition checking to C++2a Concepts Lite, then our code would no longer compile!  Definition checking means that we don't check just when we instantiate the template; the compiler would actually check the definition of the template itself and inform us that the template uses functionality that is not reflected in the Addable concept.

Proof by example:
    struct Evil1 {
        int operator+(const Evil1&) const;
        int operator+(Evil1&) = delete;
    static_assert(Addable<Evil1>);  auto failure1 = &add<Evil1>;

Proof by a different example:
    struct Evil2 {
        Evil2& operator+(const Evil2&) const;
        Evil2(Evil2&&) = default;
    static_assert(Addable<Evil2>);  auto failure2 = &add<Evil2>;

It's important to point out that C++0x Concepts weren't just check the definition to see if it's viable. They also ensure that it actually works. That is, if you had a concept like:

template <typename T>
concept Addable {
    typename result;
    result operator+(T const&, T const&);

template<Addable T>
    requires Constructible<decay_t<T::result>, T::result>
auto add(T x, T y) { return x + y; }


1) Note that I added the Constructible requirement - because the template actually needs it and wouldn't compile otherwise.
2) add<Evil1> would be perfectly fine - the x + y operation there doesn't redo overload resolution. The + itself goes through a concept map and would invoke int operator+(Evil const&) const, because that's what satisfied the concept. The int operator+(Evil&) = delete overload doesn't play here.
3) add<Evil2> would be fail because we had to add the requirement in (1) to begin with, and we would've had to add that requirement in order to get the definition of the template to compile.