namespace std {
template <class R, class F, class... ArgsTypes>
struct is_invocable_r;
template <class R, class F, class... ArgsTypes>
inline constexpr bool is_invocable_r_v = std::is_invocable_r<R, F, ArgsTypes...>::value;
}
概要
型F
が、与えられた型の引数ArgsTypes...
で関数呼び出し可能であり、その戻り値型がR
に変換可能かどうかを調べる。
要件
型R, F
およびArgsTypes...
パラメータパックの全ての型が、完全型であること。もしくはconst
/volatile
修飾された(あるいはされていない)void
か、要素数不明の配列型であること。
効果
型F
に対して引数ArgsTypes...
によるINVOKE要件に従った呼び出しが可能であり、その結果となる戻り値の型がR
に変換可能であればtrue_type
から派生し、そうでなければfalse_type
から派生する。(C++20まで。)
C++23 では、型F
に対して引数ArgsTypes...
によるINVOKE要件に従った呼び出しが可能であり、その結果となる戻り値の型をS
とすると、S
はR
に変換可能かつ、S
はR
に束縛されて寿命が延長されないのであればtrue_type
から派生し、そうでなければfalse_type
から派生する。
結果は以下と等価である:
// C++17
std::is_convertible_v<std::invoke_result_t<F, ArgsTypes...>, R>
// C++23
std::is_convertible_v<std::invoke_result_t<F, ArgsTypes...>, R> &&
std::reference_converts_from_temporary_v<R, std::invoke_result_t<F, ArgsTypes...>> == false
寿命の延長とダングリング参照
前項でのS
を継続して使用する。以下ではS
/R
をS
の値/R
の値の意味で用いる場合がある。
S
がR
に束縛されて寿命が延長されるとは、R
が参照、特にconst T&
やT&&
である場合に発生するS
の値に対する寿命の延長のことである。本メタ関数は実際にINVOKEとR
への変換を行う訳では無いが、本メタ関数に渡された型の値を使用してINVOKEとS
からR
への変換が行われる場合(つまりinvoke_r
の中)について考えてみよう。
R
がconst T&
またはT&&
で、かつS
がrvalue
であれば、S
をR
へ暗黙変換する際にS
の寿命はR
の寿命に合わせて延長されることがある。しかしこの場合、R
はINVOKEが行われる文を寿命とするので、S
も同様にINVOKEが行われる文が終了すると同時に寿命が終了する。参照による寿命の延長は2度適用されることはないため、このような場合には常にダングリング参照が生じてしまう。
これを検出し、不適格とするためにreference_converts_from_temporary_v
を使用しているというわけなのである。
#include <iostream>
#include <functional>
struct S {
S() { std::cout << "S construct" << std::endl; }
~S() { std::cout << "S destruct" << std::endl; }
};
int main()
{
using R = const S&;
std::cout << "1" << std::endl;
// INVOKE、つまり std::invoke の戻り値である S が
// R に束縛されて R の寿命と同じ寿命に延長された上で std::invoke_r の戻り値となる
// しかし R の寿命はこの1文の間だけなので、S もこの1文が終了する際に破棄される
const S& a = std::invoke_r<R>([]() { return S{}; });
std::cout << "2" << std::endl;
}
例
#include <type_traits>
#include <iostream>
auto f(int) -> double {
return 0.0;
}
int main()
{
std::cout << std::boolalpha;
//f(int) -> double
std::cout << std::is_invocable_r<double, decltype(f), int>::value << std::endl;
//f(int) -> double -> int
std::cout << std::is_invocable_r<int, decltype(f), int>::value << std::endl;
//f(int*) (定義なし)
std::cout << std::is_invocable_r<double, decltype(f), int*>::value << std::endl;
//f(int) -> double -> char* (戻り値型変換不可)
std::cout << std::is_invocable_r<char*, decltype(f), int>::value << std::endl;
}
出力
true
true
false
false
バージョン
言語
- C++17
処理系
- Clang: ??
- GCC: ??
- Visual C++: 2017 Update 3 ✅