C++2a(C++20)コンセプト requires式(requires-expression) に記述する 複合要件(compound-requirement) では「ある式の評価結果が特定コンセプトを満たすこと」を制約するが、このとき標準コンセプトstd::same_as
/std::convertible_to
を適切に使い分ける必要がある。特に “データメンバ型の制約” には注意すること。
下記コードのコンセプトC0
では式t.data
がint
型へと変換可能(convertible_to<int>
)と制約しているが、コンセプトC1
のようにint
型と等しい(same_as<int>
)と制約するとプログラマの期待通りに動作しない。
#include <concepts>
struct S {
int data;
int& mf();
};
template <typename T> concept C0 = requires (T t) {
{ t.data } -> std::convertible_to<int>;
{ t.mf() } -> std::same_as<int&>;
};
static_assert( C0<S> );
template <typename T> concept C1 = requires (T t) {
{ t.data } -> std::same_as<int>;
};
static_assert( C1<S> );
戻り値型を制約する複合要件{ E } -> C<U>;
は、2つの制約E; requires C<decltype((E)), U>;
*1と等価である。ここでdecltype指定子オペランドが括弧付きの式(E)
となることに留意。(→id:yohhoy:20200817)
template <typename T> concept C1 = requires (T t) {
t.data;
requires std::same_as<decltype((t.data)), int>;
};
クラスのデータメンバ型をsame_as
コンセプトで制約する場合は、下記コンセプトC2
のように入れ子要件(nested-requirement)を利用する。*2
template <typename T> concept C2 = requires {
requires std::same_as<decltype(T::data), int>;
}
static_assert( C2<S> );
下記コンセプトC3
のように複合要件とsame_as
コンセプトを組み合わせた場合、データメンバ型はint
またはint&
いずれも制約を満たす。これはsame_as
コンセプト利用意図からすると、不適切な制約表現といえる。*3
struct S1 {
int data;
};
struct S2 {
int& data;
};
template <typename T> concept C3 = requires (T t) {
{ t.data } -> std::same_as<int&>;
};
static_assert( C3<S1> && C3<S2> );
N4861 7.5.7.3/p1, 18.4.2, 18.4.4/p1より一部引用。
compound-requirement:
{
expression }
noexcept
opt return-type-requirementopt ;
return-type-requirement:
->
type-constraint
A compound-requirement asserts properties of the expression E
. Substitution of template arguments (if any) and verification of semantic properties proceed in the following order:
- Substitution of template arguments (if any) into the expression is performed.
- If the
noexcept
specifier is present, E
shall not be a potentially-throwing expression (14.5).
- If the return-type-requirement is present, then:
- Substitution of template arguments (if any) into the return-type-requirement is performed.
- The immediately-declared constraint (13.2) of the type-constraint for
decltype((E))
shall be satisfied. [Example: Given concepts C
and D
,
requires {
{ E1 } -> C;
{ E2 } -> D<A1, ..., An>;
};
is equivalent to
requires {
E1; requires C<decltype((E1))>;
E2; requires D<decltype((E2)), A1, ..., An>;
};
(including in the case where n is zero). -- end example]
template<class T, class U>
concept same-as-impl = is_same_v<T, U>;
template<class T, class U>
concept same_as = same-as-impl <T, U> && same-as-impl <U, T>;
[Note: same_as<T, U>
subsumes same_as<U, T>
and vice versa. -- end note]
Given types From
and To
and an expression E
such that decltype((E))
is add_rvalue_reference_t<From>
, convertible_to<From, To>
requires E
to be both implicitly and explicitly convertible to type To
. The implicit and explicit conversions are required to produce equal results.
template<class From, class To>
concept convertible_to =
is_convertible_v<From, To> &&
requires(add_rvalue_reference_t<From> (&f)()) {
static_cast<To>(f());
};
関連URL