c++templatesc++20c++-conceptsc++-templates

What is the difference between using a Concept directly instead of `typename`, versus using the `requires` keyword? Or is it just a matter of style?


I'm trying to wrap my head around concepts in C++20. Consider the following (very simple) concept:

template <typename T>
concept Float = requires
{
    std::floating_point<T>;
};

I can write a templated class using this concept in 2 ways:

template <Float T>
class MyClass {
public:
    MyClass(T val) : value{ val } {};
private:
    T value;
};

Or I can do

template <typename T> requires Float<T>
class MyClass {
public:
    MyClass(T val) : value{ val } {};
private:
    T value;
};

In this particular example, these behave exactly the same. Is this always the case though? Are there situations where it is easier to express some constraint using the template <...> requires ... syntax? Or is this merely a style preference/convention?

The only thing I can think of right now is that using the template <...> requires ... syntax may allow you represent relational constraints between multiple template arguments, where as the template <CONCEPT T> syntax only allows you to represent a constraint on a single argument?

Any guidance would be much appreciated.


Solution

  • There is no difference between:

    template <Float T>
    

    and

    template <typename T> requires Float<T>
    

    They mean the same thing. If you declare a template and then define it later, you have to use the same syntax in both places - but both syntaxes do mean the same thing.

    It's purely a matter of preference. The type-constraint syntax is more limited (can only be used to constrain a type) but can be more readable.

    Note that the type-constraint syntax can express multi-type constraints. For instance the typical ranges pattern of:

    template <typename I, typename S>
        requires input_iterator<I>
             and sentinel_for<S, I>
    void algo(I, S);
    

    can be written with the shorthand:

    template <input_iterator I, sentinel_for<I> S>
    void algo(I, S);