c++c++20template-specializationc++-concepts

Why can't we specialize concepts?


The syntax that works for classes does not work for concepts:

template <class Type>
concept C = requires(Type t) {
    // ...
};


template <class Type>
concept C<Type*> = requires(Type t) {
    // ...
};

MSVC says for the line of the "specialization": error C7606: 'C': concept cannot be explicitly instantiated, explicitly specialized or partially specialized.

Why cannot concepts be specialized? Is there a theoretical reason?


Solution

  • Because it would ruin constraint normalization and subsumption rules.

    As it stands now, every concept has exactly and only one definition. As such, the relationships between concepts are known and fixed. Consider the following:

    template<typename T>
    concept A = atomic_constraint_a<T>;
    
    template<typename T>
    concept B = atomic_constraint_a<T> && atomic_constraint_b<T>;
    

    By C++20's current rules, B subsumes A. This is because, after constraint normalization, B includes all of the atomic constraints of A.

    If we allow specialization of concepts, then the relationship between B and A now depends on the arguments supplied to those concepts. B<T> might subsume A<T> for some Ts but not other Ts.

    But that's not how we use concepts. If I'm trying to write a template that is "more constrained" than another template, the only way to do that is to use a known, well-defined set of concepts. And those definitions cannot depend on the parameters to those concepts.

    The compiler ought to be able to compute whether one constrained template is more constrained than another without having any template arguments at all. This is important, as having one template be "more constrained" than another is a key feature of using concepts and constraints.

    Ironically, allowing specialization for concepts would break (constrained) specialization for other templates. Or at the very least, it'd make it really hard to implement.