C++2a(C++20)標準ライブラリの関数仕様記述で用いられる Constraints/Mandates/Preconditions の違いについてメモ。
- 現行C++17標準ライブラリの Requires は廃止され、C++2aでは Constraints/Mandates/Preconditions に細分化される。
- Mandates: 型や定数式に対する必須要件。違反時は ill-formed のためコンパイルエラー。
- Constraints: 型や定数式に対する制約条件。違反時は関数オーバーロード候補から除外される。“SFINAE-friendly”
- Preconditions: 引数値やオブジェクト状態に対する事前条件。違反時は実行時エラーや未定義動作(undefined behavior)を引き起こす。
- C++標準規格では関数仕様のみを定め、標準ライブラリの実現方式には言及しない。
例:std::packaged_task<R(ArgTypes...)>
のテンプレートコンストラクタtemplate<class F> packaged_task(F&& f);
定義は、C++17/C++2a標準ライブラリ仕様ではそれぞれ下記の通り記述される。*4
(C++17仕様)
Requires:INVOKE<R>(f, t1, t2, ..., tN)
, wheret1, t2, ..., tN
are values of the corresponding types inArgTypes...
, shall be a valid expression. Invoking a copy off
shall behave the same as invokingf
.
Remarks: This constructor shall not participate in overload resolution ifdecay_t<F>
is the same type aspackaged_task<R(ArgTypes...)>
.
(C++2a仕様)
Constraints:remove_cvref_t<F>
is not the same type aspackaged_task<R(ArgTypes...)>
.
Mandates:is_invocable_r_v<R, F&, ArgTypes...>
istrue
.
Preconditions: Invoking a copy off
behaves the same as invokingf
.
提案文書(PDF)P0788R3, §3 Proposed principles and practices より一部引用(下線部は強調)。注:C++2aではContracts導入が見送られたため*5、Expects: と Ensures: はそれぞれ Preconditions: と Postconditions: が対応する。
I. Let's not recycle a Requires: element to mean something other than what it means today.
- a) Let's instead adopt new elements, described below, to specify the Library requirements that are (or that should have been) specified via our current Requires: elements. [(snip)]
- (snip)
II. Let's introduce a new Constraints: element.
- a) Let's use this Constraints: element to specify the compile-time circumstances that must be satisfied in order that the corresponding Library component will be compiled. [(snip)]
- b) Let's ensure that unsatisfied Constraints: not produce any diagnostic in and of themselves.
[This obviates the need for specification wording such as "shall not participate in overload resolution." Note that a consequential diagnostic might still result: for example, overload resolution might find no viable candidates due to unsatisfied constraints and/or other factors.]- c) Let's introduce a new Mandates: element to specify the compile-time circumstances under which, when unsatisfied, an implementation must produce a diagnostic.
[(snip)]
[This element obviates the need for any "is ill-formed" specifications. For example, in [pair.astuple]/1 we today find the specification "Requires:I < 2
. The program is ill-formed if I is out of bounds." Under the present proposal, this would be simplified to "Mandates:I < 2
."]
III. Let's introduce a new Expects: element.
- a) Let's use this Expects: element to specify the circumstances that must be satisfied to avoid undefined behavior when the corresponding Library component is invoked.
[Industry-wide, such requirements have come to be known as preconditions, but the Contracts proposals [P0542R1] seem to have chosen "expects" as their preferred term of art; it seems better to have a single term and use it consistently.]- (snip)
IV. Let's avoid any specification that demands any particular technology by which implementations must comply with Library specifications.
- a) Let's permit an implementation to use a requires-clause, an
enable_if
, a constexpr if, or any other technology or combination of technologies to meet Constraints: specifications.- b) Let's permit an implementation to use
static_assert
and/or any other technologies to meet Mandates: specifications.- c) Let's permit an implementation to use Contracts attributes [P0542R1] and/or any other technologies to meet Expects: and Ensures: specifications.
- d) Let's consider user code that relies on any specific technology on the part of an implementation to be ill-formed, with no diagnostic required.
C++17仕様
C++17 20.4.1.4/p3-4より一部引用。
3 Descriptions of function semantics contain the following elements (as appropriate):
- Requires: the preconditions for calling the function
- (snip)
4 (snip) If
F
's semantics specifies a Requires: element, then that requirement is logically imposed prior to the equivalent-to semantics. (snip)
C++2a仕様
C++2a DIS(N4861) 16.4.1.4/p3より一部引用。
3 Descriptions of function semantics contain the following elements (as appropriate):
- Constraints: the conditions for the function's participation in overload resolution (12.4). [Note: Failure to meet such a condition results in the function's silent non-viability. --end note] [Example: An implementation might express such a condition via a constraint-expression (13.5.2). --end example]
- Mandates: the conditions that, if not met, render the program ill-formed. [Example: An implementation might express such a condition via the constant-expression in a static_assert-declaration (9.1). If the diagnostic is to be emitted only after the function has been selected by overload resolution, an implementation might express such a condition via a constraint-expression (13.5.2) and also define the function as deleted. --end example]
- Preconditions: the conditions that the function assumes to hold whenever it is called.
- (snip)
4 (snip) If
F
's semantics specifies any Constraints or Mandates elements, then those requirements are logically imposed prior to the equivalent-to semantics. (snip)
関連URL
*1:https://cpprefjp.github.io/lang/cpp20/concepts.html
*2:https://cpprefjp.github.io/reference/type_traits/enable_if.html
*3:https://cpprefjp.github.io/lang/cpp17/if_constexpr.html
*4:C++17 “INVOKE<R>(...) shall be a valid expression” は、C++2a “is_invocable_r_v<R, ...> is true” に対応する。
*5:https://cpprefjp.github.io/lang/cpp23/contract-based_programming.html