最終更新日時(UTC):
が更新

履歴 編集

class template
<type_traits>

std::is_invocable_r(C++17)

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とすると、SRに変換可能かつ、SRに束縛されて寿命が延長されないのであれば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/RSの値/Rの値の意味で用いる場合がある。

SRに束縛されて寿命が延長されるとは、Rが参照、特にconst T&T&&である場合に発生するSの値に対する寿命の延長のことである。本メタ関数は実際にINVOKERへの変換を行う訳では無いが、本メタ関数に渡された型の値を使用してINVOKESからRへの変換が行われる場合(つまりinvoke_rの中)について考えてみよう。

Rconst T&またはT&&で、かつSrvalueであれば、SRへ暗黙変換する際にSの寿命はRの寿命に合わせて延長されることがある。しかしこの場合、RINVOKEが行われる文を寿命とするので、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

処理系

参照