Requirements on Return Types

Get an overview of the requirements on the return types.

We'll cover the following

We’ve seen how to write a requirement expressing that certain functions must exist in a class API.

Constrain the return type

But did we constrain the return type of those functions?

template <typename T>
concept HasSquare = requires (T t) {
  t.square();
  t.sqrt();
};

No, we didn’t. A class would satisfy the constraints of the HasSquare concept with int square() and void square() both.

If we want to specify the expected return type, we must use a compound requirement.

Here is an example:

template <typename T>
concept HasSquare = requires (T t) {
    {t.square()} -> std::convertible_to<int>;
}; 

Notice the following:

  • The expression we want to set a return type requirement on must be surrounded by braces ({}), followed by an arrow (->). The constraint on the return type will come at the end.
  • A constraint cannot simply be a type. If we just write int, we’d get the following error message: “return-type-requirement is not a type-constraint.” The original concepts TS allowed the direct usage of types, so if we experimented with that, we might be surprised by this error. This possibility was removed by P1452R2.

There are a number of reasons for this removal. One reason was that it would interfere with a future direction of wanting to adopt a generalized form of auto, like vector<auto> or vector<Concept auto>.

So, instead of simply naming a type, we have to choose a concept! If we want to set the return type, we have the following two choices:

{t.square()} -> std::same_as<int>;
{t.square()} -> std::convertible_to<int>;

In the case of std::same_as, the return value must be the same as specified in the template argument, while with std::convertible_to conversions are allowed.

To demonstrate this, let’s have a look at the following example:

Get hands-on with 1200+ tech skills courses.