yohhoyの日記

技術的メモをしていきたい日記

std::functionの戻り値型に関する制約条件

C++11標準ライブラリ提供のstd::functionクラステンプレートにおいて、同functionオブジェクトで保持する呼び出し可能オブジェクト(callable object)の戻り値型に関するメモ。

2015-09-27追記:この制限は次期標準規格C++1z(C++17)にて修正される。id:yohhoy:20150927参照。

下記サンプルコードにおいて、オブジェクトf1のコンストラクタ呼び出しはその要件に違反しており、厳密にC++11標準規格に従うと未定義動作(undefined behavior)を引き起こす。これは、クラステンプレート引数に与えた関数シグネチャの戻り値型R = voidに対して、関数twiceの戻り値型intからの暗黙的な変換(intvoid)が定義されないことによる。

#include <functional>

int twice(int x)
{ return x * 2; }

int main()
{
  std::function<int(int)> f0( twice );   // OK
  std::function<void(int)> f1( twice );  // NG: int→voidへ暗黙変換不可

  f0(42);
  f1(42);
}

ただし、gcc/libstdc++など大抵の処理系では上記コードを許容し、(プログラマの期待通り)戻り値を破棄するよう動作してしまう。LLVM/libc++ではコンパイルエラーとして検知するとのこと*1

N3337 17.6.4.11/p1, 20.8.2/p2, 20.8.11.2/p2, 20.8.11.2.1/p7より一部引用。

1 Violation of the preconditions specified in a function's Requires: paragraph results in undefined behavior unless the function's Throws: paragraph specifies throwing an exception when the precondition is violated.

2 Define INVOKE(f, t1, t2, ..., tN, R) as INVOKE(f, t1, t2, ..., tN) implicitly converted to R.

2 A callable object f of type F is Callable for argument types ArgTypes and return type R if the expression INVOKE(f, declval<ArgTypes>()..., R), considered as an unevaluated operand (Clause 5), is well formed (20.8.2).

template<class R, class... ArgTypes>
class function<R(ArgTypes...)>

template<class F> function(F f);
7 Requires: F shall be CopyConstructible. f shall be Callable (20.8.11.2) for argument types ArgTypes and return type R. (snip)

関連URL