5. 制約をかける
Concepts Lite Syntax
制約違反の際、分かりやすい診断メッセージを出力可能
list<int> lst = ...;
sort(lst); // Error
error: no matching function for call to `sort(list<int>&)'
sort(l);
^
note: candidate is:
note: template<Sortable T> void sort(T)
void sort(T t) { }
^
note: template constraints not satisfied because
note: `T' is not a/an `Sortable' type [with T = list<int>]
note: failed requirement with ('list<int>::iterator i', std::size_t n)
note:
`i[n]' is not valid syntax
2014/1/30
5
6. 制約をかける
Concepts Lite Syntax
関数テンプレート
クラステンプレート
requires-clauseにより制約を記述
template<typename Cont>
requires Sortable<Cont>()
void sort(Cont& container);
template<typename T, typename A>
requires Object<T>() && Allocator<A>()
class vector;
typenameの箇所にconstraintを記述
template<Sortable Cont>
void sort(Cont& container);
template<Object T, Allocator A>
class vector;
templateの代わりにconcept-introductionを用いて記述
Sortable{Cont}
void sort(Cont& container);
Object{T} && Allocator{A}
class vector;
2つ以上の独立した制約がある場合、
この表記は使えない
詳細は後述
2014/1/30
6
10. 制約をかける
関連するWording
PDF P46 Standard
Wording
7.1.6.5 Constrained type specifiers [dcl.spec.constrained]
The type denoted by a concept-name or partial-concept-id is a constrained placeholder
types. A constraint is formed from the application of the concept to the
placeholder type. The placeholder type is used as the first template argument
of the constraint. If the type specifier is a partial-concept-id, the specified arguments follow the placeholder type in the formed constraint. [Example:
Real y = f(x); // decltype(y) is a placeholder type,
後で説明
// constrained by Real<decltype(y)>
Same_as<T> a = f(b); // decltype(a) is a placeholder type,
// constrained by Same_as<T, decltype(a)>
— end example]
例がdecltype絡みばかりで
分かりにくい?
逆ではないか
Same_asとはいえ
2014/1/30
10
13. 制約をかける
A
制約のかけ方まとめ
parserのカレントカーソルの
進む向きと考えると混乱する
冗長だが、全ての制約表記が可能
以前に定義されている
今後、定義されるかも
と時間軸で
とらえると分かりやすい
template <typename T, typename U>
requires Constraint1<T, U>() &&
Constraint2<U, T>()
name;
class hoge; // forward decl
Conceptの引数が1つの場合、読みやすい記述が可能
前方
template <Constraint1 T, Constraint2 U>
name;
B
後方
typedef hoge fuga;
class hoge {};
Conceptの引数の後方参照がある場合、表現できない
C
Conceptが1つで、全ての引数が制約されている場合に、読みやすい記述が可能
Constraint{T, U}
name;
制約されない引数がある場合表現できない。
Conceptが複数ある場合、表現できない
A
は B
と混ぜることができる
A
template <Constraint1 T, typename U>
requires ConstraintX<T>() && ConstraintY<U>()
name;
要確認
2014/1/30
OK
と C
は混ぜることができない
Constraint{T}
requires ConstraintX<T>()
name
13
14. 制約のかけ方まとめ
制約をかける
A
冗長だが、全ての制約表記が可能
template <typename T, typename U>
requires Constraint1<T, U>() &&
Constraint2<U, T>()
name;
割と何でも書けそう
14 Templates [temp]
P48
template-declaration:
template < template-parameter-list > requires-clauseopt declaration
concept-introduction declaration
requires-clause:
requires-clause:
requires constant-expression
constant-expression
の誤りではないだろうか?
A template declaration with a requires-clause is a constrained template. A
requires-clause introduces template constraints in the form of a constant-expression
whose result type is bool.
A declaration introduced by a concept-introduction (14.9.5) is a constrained
template.
例えばこれは許される?
template <typename T>
true
name;
template <typename T>
is_pointer<T>::value
name;
2014/1/30
template <typename T>
requires (T a, T b) {
{ a == b } -> bool;
}
name;
全ての例が
requiresから
始まってるし
この記述方法に
ついては後述
14
15. 制約をかける
B
制約のかけ方まとめ
Conceptの引数が1つの場合、読みやすい記述が可能
template <Constraint1 T, Constraint2 U>
name;
14.1 Template parameters [temp.param]
P48
conceptキーワードを使って定義した
concept definitionしか書けない
constexpr関数名はNG
7.1.6 Simple type specifiers [dlt.type.simple]
type-name:
...
concept-name
partial-concept-id
concept-name:
identifier
partial-concept-id:
concept-name < template-argument-list >
P46
template-parameter:
type-parameter
parameter-declaration
constrained-parameter
constrained-parameter:
constraint-id ...opt identifier
constraint-id ...opt identifier = constrained-default-argument
constraint-id:
concept-name
partial-concept-id
constrained-default-argument:
type-id
template-name
A constrained-parameter is introduced by a constraint-id, which is either a
concept-name or a partial-concept-id. The concept definition referred to by the
constraint-id determines the kind of template parameter and the constraints
applied to that argument.
P47
7.1.7 The concept specifier [dcl.concept]
The concept specifier shall be applied to only the definition of a function
template. A function template definition having the concept specifier is called a
concept definition.
2014/1/30
イタリックにすべき?
15
16. 制約をかける
C
制約のかけ方まとめ
Conceptが1つで、全ての引数が制約されている場合に、読みやすい記述が可能
Constraint {T, U}
name;
14 Templates [temp]
conceptキーワードを使って定義した
concept definitionしか書けない
constexpr関数名はNG
P48
template-declaration:
template < template-parameter-list > requires-clauseopt declaration
concept-introduction declaration
requires-clause:
constant-expression
14.9.5 Concept Introductions [temp.con.intro]
P54
concept-introduction:
concept-name { introduced-parameter-list }
introduced-parameter-list:
identifier
introduced-parameter-list , identifier
The concept name is matched, based on the number of introduced parameters
to a corresponding concept definition. If no such concept can be found, the
program is ill-formed.
The kind of each introduced parameter (type, non-type, template), is the same
as the corresponding template parameter in the matched concept definition.
The concept is applied introduced parameters as a constraint on the trailing
declaration.
2014/1/30
イタリックにすべき?
16
19. 制約をかける
Auto
autoが書けるところ全てを、conceptで置き換え可能にしようというアイデア
generic lambdaを推し進めると次のように書けるようになる?
制約あり
制約無し
auto gcd(auto a, auto b);
Integer gcd(Integer a, Integer b);
Integer制約を
かける
上記は、下記と同義
template<typename T, typename U>
auto gcd(T a, U b);
上記は、下記と同義
template<Integer T>
T gcd(T a, T b);
制約無し
template <typename T>
auto begin(T&& t) -> decltype(t.begin());
Integer制約
template <typename T>
Iterator begin(T&& t) -> decltype(t.begin());
戻り値推論で
template <typename T>
Iterator begin(T&& t);
A の書き方なら
2014/1/30
template <typename T>
requires Iterator<decltype(declval<T>().begin())>()
auto begin(T&& t);
19
20. 制約をかける
Auto
変数も制約可能
auto y = f(x);
Real y = f(x);
Random_access_iterator r = find(p,q,v);
関数のオーバーロードセットを制約されたテンプレートに渡すことが可能
vector<double> v { ... };
Number n = accumulate(v.begin(), v.end(), operator*);
Number n = accumulate(v.begin(), v.end(), [](auto a, auto b) { return operator*(a, b)});
accumulateのテンプレートパラメタが制約されていることが必須とのこと
(詳細は不明)
2014/1/30
20
21. 制約の内容
制約の定義(concept)
template<typename T>
concept bool Equality_comparable() { ... }
P47
7.1.7 The concept specifier [dcl.concept]
The concept specifier shall be applied to only the definition of a function template. A function template definition having the concept specifier is called a
concept definition.
Every concept definition is also a constexpr declaration (7.1.5).
Concept definitions have the following restrictions:
The template must be unconstrained.
The result type must be bool.
The declaration may have no function parameters.
may?
The declaration must be defined.
The function shall not be recursive.
制約をconceptとして定義することで、以下の表記が可能となる
B
2014/1/30
template <Constraint1 T, Constraint2 U>
name;
C
Constraint{T, U}
name;
21
22. 制約の内容定義
制約の内容
B
C
の表記を使わないのであれば constexpr でもよい
template<typename T>
concept bool Constraint()
{
return requires (T a, T b) {
a++;
a != b;
// simple requirement
{a == b} -> bool;
{ ++a } -> T&;
{ T(a) } noexcept constexpr;
// compound-requirement
typename T::value_type;
// type-requirement
requires Other_constraint<T>(); // nested-requirement
};
}
requres-expression
2014/1/30
22
23. 制約の内容定義
制約の内容
template<typename T>
concept bool Constraint()
{
return requires (T a, T b) {
a++;
a != b;
{a == b} -> bool;
{ ++a } -> T&;
{ T(a) } noexcept constexpr;
typename T::value_type;
5.1.3 Requires expressions [expr.prim.requires]
A requires expression provides a concise way to express
syntactic requirements
for template constraints. [Example:
template<typename T>
constexpr bool Readable() {
かなり意味不明
return requires (T i) {
(後ほど再検討)
// simple requirement
typename Value_type<T>;
const Value_type<T>& = {*i};
};
// compound-requirement
}
— end example]
P45
// type-requirement
requires Other_constraint<T>(); // nested-requirement
};
}
2014/1/30
P45
A requires-expression shall only appear inside a template.
The type of a requires-expression is bool, and it is a constant expression.
The requires-expression may be introduce local arguments via a parameterdeclaration-clause. These parameters have no linkage, storage, or lifetime. They
are used as notation for the purpose of writing requirements.
The body of requires-expression is a list of requirements. If each requirement in
that list is satisfied, the result of the expression is true, or false otherwise.
テンプレートの中にしか書けない(テンプレートの中ならconceptの中じゃなくても書ける)
式の値の型はbool。引数は、制約記述のためにローカル変数的に用いられ、リンケージや
ライフタイムは持たない。内容は制約のリストで、全リストがtrueならrequires-expressionの値が
trueとなり、そうでなければ falseとなる。
23
24. 制約の内容定義
制約の内容
template<typename T>
concept bool Constraint()
{
return requires (T a, T b) {
a++;
a != b;
// simple requirement
P45
5.1.3.1 Simple requirements [expr.req.simple]
{a == b} -> bool;
// compound-requirement
A simple-requirement introduces a requirement that the expression is a valid
{ ++a } -> T&
expression } noexcept constexpr; instantiation of the constraint results in
{ T(a) when instantiated. If the
a substitution failure, the the requirement is not satisfied.
typename T::value_type;
// type-requirement
requires Other_constraint<T>(); // nested-requirement
};
}
2014/1/30
24
25. 制約の内容
制約の内容定義
P45
5.1.3.2 Compound requirements [expr.req.compound]
A compound-requirement introduces a set of requirements involving a single
template<typename T>
expression. The expression must compile when instantiated.
concept bool Constraint()
If a result-type-requirement is present then the result type of the instantiated
{
expression must satisfy that requirement. If the required type-id is a constrained
return requires (T a, those constraints must also be satisfied.
placeholder type (7.1.6.5), thenT b) {
a++;
// simple requirement
If the constexpr specifier is present, the instantiated expression must be constexpr-evaluable.
a != b;
If the noexcept specifier is present, instantiated expression must not propagate exceptions.
{a == b} -> bool;
{ ++a } -> T&;
{ T(a) } noexcept constexpr;
2014/1/30
// compound-requirement
戻り値がboolに変換可能なら制約を満たすといったケースを
satisfy that requirementでは明言できていない気がする。
that requirementはresult-type-requirementを指すはず。
7.1.6.5 Constrained type specifiers [dcl.spec.constrained]
typename T::value_type;
// type-requirement
P46
The type denoted by a concept-name or partial-concept-id is a constrained placeholder types. A
onstraint is formed from the application of the concept to the placeholder type. The placeholder type
requires Other_constraint<T>(); // nested-requirement
is used as the first template argument of the constraint. If the type specifier is a partial-concept-id, the
specified arguments follow the placeholder type in the formed constraint. [Example:
};
Real y = f(x); // decltype(y) is a placeholder type,
}
// constrained by Real<decltype(y)>
Same_as<T> a = f(b); // decltype(a) is a placeholder type,
戻り値が別のConstraind placeholder typeの場合は
// constrained by Same_as<T, decltype(a)>
ここで説明できている
— end example]
The first use of concept-name or partial-concept-id within a scope binds that name to the placeholder
type so that subsequent uses of the same name refer to the same type. [Example:
template<typename T>
concept bool Number() { ... }
auto mul = [](Number a, Number b) { return a * b; }
The types of a and b are the same. This is equivalent to having written:
auto mul = []<Number N>(N a, N b) { return a * b; }
— end example]
25
26. 制約の内容定義
制約の内容
template<typename T>
concept bool Constraint()
{
return requires (T a, T b) {
a++;
a != b;
// simple requirement
{a == b} -> bool;
{ ++a } -> T&;
{ T(a) } noexcept constexpr;
typename T::value_type;
}
2014/1/30
// compound-requirement
// type-requirement
P45
5.1.3.3 Type requirements [expr.req.type]
requires Other_constraint<T>(); // nested-requirement
A type-requirement introduces a requirement that an associated type name can
be formed when instantiated. If the instantiation of the type requirement results
}; in a substitution failure, the requirement is not satisfied.
26
27. 制約の内容
制約の内容定義
template<typename T>
concept bool Constraint()
{
return requires (T a, T b) {
a++;
a != b;
// simple requirement
{a == b} -> bool;
{ ++a } -> T&;
{ T(a) } noexcept constexpr;
// compound-requirement
typename T::value_type;
// type-requirement
requires Other_constraint<T>(); // nested-requirement
P46
5.1.3.4 Nested requirements [expr.req.type]
}; nested requirement introduces additional constraints to be evaluated as part
A
} of the requires expression. The requires-clause is constexpr evaluated, and the
requirement is satisfied only when that evaluation yields true.
2014/1/30
27
29. オーバーロードの解決
logical-and
&&
logical-or
concept check
||
Concept_name<T>()
左記以外の expression
atomic proposition
14.9.1 Constraint reduction [temp.con.reduce]
A constrained template’s actual constraints are reduced by inlining concept
checks, and producing an expression comprised of only atomic propositions and logical-andexpressionss and logical-or-expressions.
reduceされたイメージ
&&
&&
atomic proposition
||
atomic proposition
14.9.2 Constraint Satisfaction [temp.con.sat]
A template’s constraints are satisfied if the constexpr evaluation of the reduced
constraints results in true.
2014/1/30
P52
P52
29
30. オーバーロードの解決
14.5.6.2 Partial ordering of function templates [tmp.func]
Partial ordering selects which of two function templates is more specialized
than the other by transforming each template in turn (see next paragraph)
and performing template argument deduction using the function type. The
deduction process determines whether one of the templates is more specialized
than the other. If so, the more specialized template is the one chosen by the
partial ordering process. If both deductions succeed, the the more specialized
template is the one that is more constrained (14.9.3).
P51
より制約された方が、よりspecializedされたテンプレートである
2014/1/30
30
31. オーバーロードの解決
P53
14.9.3 Partial ordering of constraints [temp.con.ord]
Partial ordering of constraints is used to choose among ambiguous specializations during the partial ordering of
function templates, the partial ordering of class templates, and the use of template template arguments. The
partial ordering of two reduced constraints P and Q determines if P subsumes Q.
Determining the partial ordering of reduced constraints requires their decomposition into a list of lists of atomic
propositions. Decomposition of a reduced constraint P begins with a single list (the current list) containing the
reduced constraint P (the current term). Decomposition proceeds by analyzing the current
term.
If P is of the form a && b, then replace P in the current list with the operands a and b so that a is the current
term and b will be the next term.
If P is of the form a || b, then create a copy of the current list, replacing P with a in the original and replacing
P with b in the copy so that a is the current term in the original and b is the current term in the copy.
Otherwise, advance to the next term. If there are no remaining terms, advance to the next list. If there are
no remaining lists, decomposition terminates.
Given two reduced constraints P and Q, then P subsumes Q only if all of Q’s atomic propositions can be found in
the P. Denote this comparison as P ⊢ Q.
This is computed by first decomposing P into a list of lists of atomic propositions, L, and then comparing the
expression Q against each list Li in L, which is equivalent to determining if Li ⊢ Q according to the following rules:
If Q is of the form a && b, then Li ⊢ Q if and only if Li ⊢ a and Li ⊢ b.
If Q is of the form a || b, then Li ⊢ Q if and only if Li ⊢ a or Li ⊢ b.
Otherwise Li ⊢ Q if and only if there exist some atomic proposition A in Li that matches Q (14.9.4).
Only when each Li ⊢ Q does P ⊢ Q.
For two template declarations with equivalent type, the first is at least as constrained as the second if both
templates are unconstrained, the first is constrained and the second unconstrained, or the constraints of the first
subsume the constraints of the second.
2014/1/30
x ⊢ y means y is derivable from x.
y は x から推論できる。
31
32. オーバーロードの解決
A && B && C
A && B || C
&&
A || B && C
||
&&
&&
&&
A
B
A
C
B
C
||
A
B
A
C
A
B
B
A
atomic proposition の 集合 のリストを形成する
C
B
C
C
C
A || (B && C)
||
集合
A
B
&&
リスト
集合
C
A
B
B
C
C
A
2014/1/30
32
33. オーバーロードの解決
満たされている制約
テンプレートの制約
P
A || B
A
Q
A
P
A
A
A
B
Q
A && B
A
B
A
A
B
P
A
B
P
A
B
Q
A
B
Q
A
B
Qがより制約されている
Q は P に含まれる
P は Qに含まれない
2014/1/30
Pがより制約されている
Pを満たすなら常にQ満たす
Qを満たしてもPを満たして
いるとは限らない
P は Q に含まれる
Q は Pに含まれない
満たされている制約は候補に挙げるか否かの判断に使うだけで、
どちらがより制約されているかの判断には使わない
確信が持てない
33
34. オーバーロードの解決
P
A
B
C
Q
A || (B && C)
A
B
A
C
P
(A && B) || C
B
A
B
C
A && B
A
Q
C
B
C
C
A
A
P
B
C
B
A
A
Q
B
B
C
PQ同レベル
2014/1/30
C
C
PQ同レベル
34
35. オーバーロードの解決
P
Q
A && B || C
A && B && C
||
&&
&&
&&
A
B
A
A
C
B
B
A
B
C
C
C
満たすatomic
proposition
選択されるオーバーロード
A
-
-
B
-
-
C
P
P: P.C
AB
P
P: P.AB
BC
P
P: P.C
CA
P
P: P.C
ABC
2014/1/30
成立する
制約
P,Q
Q: Q.ABC (P.AB と P.C の両方が Q.ABCに包含される)
35
36. オーバーロードの解決
P
Q
(A && B&& C) || D || E
A
B
(A && B)|| C || (D && E)
C
A
D
A
C
E
D
B
B
D
E
A
C
B
E
D
C
満たすatomic
proposition
成立する
制約
選択されるオーバーロード
A
-
-
C
Q
Q
D
P
P
ABC
P,Q
両方
DE
P,Q
ABCD
P,Q
ABCDE
2014/1/30
E
P,Q
もし、非テンプレート引数のimplicit conversionが絡まなければ、
両方== 曖昧 でエラーとなる
implicit conversionが関係するケースは次ページで検討
36
37. オーバーロードの解決
Container ⊃ Equality_comparable
// #1
template <Container C>
void foo(C c, int i);
// #2
template <Equality_comparable Ec>
void foo(Ec ec, double d);
std::vector<int> v;
foo(v, 1);
// call #1
foo(v, 12.3); // call #1
vは#1に、よりマッチする。12.3 は #2に、よりマッチする。
今回新たに導入された制約のマッチ度合いと、これまでも存在したimplicit conversionの必要性
によるマッチ度合いがどう評価されるのか記述が見つからない。
https://groups.google.com/a/isocpp.org/forum/#!topic/concepts/67H0UOXe03g
によれば、非テンプレート引数のConversionの方が、Constraintよりも優先されるとのこと。
P293
13.3 Overload resolution [over.match]
But, once the candidate functions and argument lists have been identified, the selection of the best function
is the same in all cases:
— First, a subset of the candidate functions (those that have the proper number of arguments and meet
certain other conditions) is selected to form a set of viable functions (13.3.2).
— Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed
to match each argument to the corresponding parameter of each viable function.
2014/1/30
37
39. Standard Wording への指摘
P46
7.1.6.5 Constrained type specifiers [dcl.spec.constrained]
The type denoted by a concept-name or partial-concept-id is a constrained place-holder
types. A constraint is formed from the application of the concept to the
placeholder type. The placeholder type is used as the first template argument
of the constraint. If the type specifier is a partial-concept-id, the specified arguments follow the placeholder
type in the formed constraint. [Example:
Real y = f(x); // decltype(y) is a placeholder type,
// constrained by Real<decltype(y)>
Same_as<T> a = f(b); // decltype(a) is a placeholder type,
// constrained by Same_as<T, decltype(a)>
— end example]
逆ではないか
Same_asとはいえ
Concept_name { a, b} という表記がある今、
そもそも、この関数の部分適用みたいなの必要なの?という議論もある
2014/1/30
39
40. Standard Wording への指摘
P47
7.1.7 The concept specifier [dcl.concept]
The concept specifier shall be applied to only the definition of a function template. A function template definition having the concept specifier is called a
concept definition.
Every concept definition is also a constexpr declaration (7.1.5).
Concept definitions have the following restrictions:
The template must be unconstrained.
The result type must be bool.
shall have または must have ではないか?
The declaration may have no function parameters.
The declaration must be defined.
The function shall not be recursive.
2014/1/30
40
43. Standard Wording への指摘
14.3.3 Template template arguments [tmp.arg.template]
A template-argument matches a template template-parameter (call it P) when
each of the template parameters in the template-parameter-list of the templateargument’s
corresponding class template or alias template (call it A) matches
the corresponding template parameter in the template-parameter-list of P, and
when P is not less constrained (14.9.3) than A. [Example
// ... from standard
P50
template<template<Copyable>> class C>
class stack { ... };
template<Regular T> class list1;
template<Object T> class list1;
list2の誤りと思われる
stack<list1> s1; // OK: Regular is more constrained than Copyable
stack<list2> s2; // error: Object is not more constrained than Copyable
— end example]
14.9.5 Concept Introductions [temp.con.intro]
P54
concept-introduction:
concept-name { introduced-parameter-list }
introduced-parameter-list:
identifier introduced-parameter-list , identifier
改行が必要と思われる
2014/1/30
43