1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
When return types of operations must satisfy certain requirements then
em(compound requirements) should be used. Compound requirements define type
constraints on expressions embedded in compound statements. The C2a standard
defines several concepts that can be used to specify such requirements (see
also section ref(CXXCONCEPTS) below). Here is an example:
verb( template <typename Type, typename ReturnType>
concept Return =
requires(Type par)
{
// par[..] must return a `ReturnType'
{ par[0] } -> std::same_as<ReturnType>;
};)
This concept can now be used to specify requirements of template type
parameters. E.g.,
verb( template <typename Type, typename RetType>
requires Return<Type, RetType>
Ret fun(Type tp)
{
return tp[0];
})
Here arguments passed to tt(fun) must satify two requirements:
itemization(
it() they must provide an index operator accepting integral argument
values;
it() their index operators must return tt(std::string) values.
)
You may have noticed that the tt(std::same_as) concept receives only one
template type argument, which (as if by magic) compares it with the type
returned by the tt(par[0]) expression. When peeking at the available concepts
in section ref(CXXCONCEPTS) you will see that several of those concepts in
fact define two template type parameters. When these concepts are used in
compound requirements then the compiler passes the deduced type of the
expression in the concept's compound statement (so that's the type of
tt(par[0]) in the above example) to the concept's first type, and passes the
explicitly specified type to the concept's second type.
Knowing this we can define our own concepts to use in compound
expressions. We may define our own tt(same_as) concept as follows, using a
separate class template tt(SameTypes). tt(SameTypes) defines a
tt(bool) value `tt(value)' which is used to decide about the concept's
requirement. The class template tt(SameTypes) uses a specialization to handle
the situation where both types are equal. Note that concepts
hi(concept: specialization) themselves cannot be specialized:
verb( template <typename Lhs, typename Rhs>
struct SameTypes // generic: any two types
{
static bool const value = false;
};
template <typename Lhs>
struct SameTypes<Lhs, Lhs> //specialization: equal types
{
static bool const value = true;
};
template<typename Compound, typename Specified>
concept Same = SameTypes::value;)
Now the concept tt(Same) can be used instead of tt(std::same_as) by merely
specifying the required type:
verb( template <typename Type, typename ReturnType>
concept Return =
requires(Type par)
{
// par[..] must return a `ReturnType'
{ par[0] } -> Same<ReturnType>;
};)
Although in this case it isn't important which actual type is used as
argument for which concept type parameter, the compiler specifies the compound
expression's type as template argument for tt(Same's Compound) parameter
whereas tt(ReturnType) is used as template argument for tt(Same's Specified)
parameter.
Multiple type requirements can be specified by providing multiple compound
requirements as in the following example:
verb( template <typename Type>
concept MultiArgs =
requires(Type lhs, Type rhs)
{
{ lhs + rhs } -> std::same_as<Type>;
{ lhs += rhs } -> std::same_as<Type &>;
{ lhs.c_str() } -> std::same_as<char const *>;
};)
If it is required that the compound operation doesn't throw exceptions then
tt(noexcept) can be written immediately following the compound requirement's
late return type arrow (tt(->)). The tt(noexcept) specification itself may
then optionally be followed by a type constraint.
Finally, the late return type specifications itself is optional, in which case
the compound requirement acts like a simple requirement: it requires the
existence of the expression that's specified in the compound statement. In
this case: don't forget to add the semicolon following the closing parenthesis
of the compound requirement:
verb( template <typename Type>
concept Increment =
requires(Type par)
{
{ ++par };
// same as:
++par;
};)
|