最終更新日時:
が更新

履歴 編集

class template
<type_traits>

std::result_of(C++11)(C++17で非推奨)

namespace std {
  template <class> class result_of; // 宣言のみ

  template <class F, class... ArgTypes>
  class result_of<F(ArgTypes...)> {
    using type = ;
  };

  template <class T>
  using result_of_t = typename result_of<T>::type;
}

この機能はC++17から非推奨となった。代わりにstd::invoke_resultを使用すること。

概要

関数の戻り値の型を取得する。

要件

  • C++11まで : 型Fは、関数または関数オブジェクトであること。もしくは、型Fは、関数または関数オブジェクトへの参照であること。INVOKE(declval<Fn>(), declval<ArgTypes>()...)が有効な式であること。
  • C++14から : 型FおよびArgsTypes...パラメータパックの全ての型が、完全型であること。もしくはconst/volatile修飾された(あるいはされていない)voidか、要素数不明の配列型であること。
    • このバージョンでは要件が緩和され、関数呼び出しが可能であることが要件から外れた。これにより、有効でない関数オブジェクト、引数を指定した場合に、static_assertでコンパイルエラーにならず、テンプレートの置き換え失敗によりSFINAEが働くようになった。

効果

result_ofは、関数または関数オブジェクトの型Fに対して、ArgTypes...を引数として渡した場合の戻り値の型を、メンバ型typeとして定義する。

メンバ型typeは、以下と同じ型になる:

decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...))

C++14以降では、上記メンバ型typeの型定義が有効な式でない場合、メンバ型typeは定義されない。

非推奨の詳細

C++17で特定のシグニチャで関数呼び出しが可能かを判定するis_callableを導入する予定だったが、std::invoke()関数を導入する際に、result_ofも含めて命名規則を統一することとなった。

is_callablestd::is_invocableという名前で導入された。

result_ofは、シグニチャであることをわかりやすくするために、関数型でユーザーにテンプレート引数を指定させていたが、これは混乱の元であった。

そのため、std::invoke_resultに名称変更することとなった。

#include <iostream>
#include <string>
#include <type_traits>
#include <functional>

// 関数
int func(int a, int b)
{ return a + b; }

// 関数オブジェクト
struct functor {
  int operator()(int a, int b) const
  {
    return a + b;
  }

  // オーバーロードしている
  std::string operator()(std::string a, std::string b) const
  {
    return a + b;
  }
};

struct X {
  // メンバ関数
  int foo(int a, int b) const
  {
    return a + b;
  }
};

// 受け取った関数を呼び出し、その関数の戻り値を返す
template <class F, class... Args>
typename std::result_of<F(Args...)>::type
  invoke(F&& f, Args... args)
{
  return f(args...);
}

// Fがメンバ関数ポインタの場合
template <class F, class... Args>
typename std::result_of<F(Args...)>::type
  invoke_memfun(F&& f, Args... args)
{
  return std::bind(std::move(f), args...)();
}

int main()
{
  // 関数
  int result1 = invoke(func, 1, 2);

  // 関数オブジェクト
  int result2 = invoke(functor(), 1, 2);

  // オーバーロード
  std::string result3 = invoke(functor(), "Hello ", "World");

  // メンバ関数
  X x;
  int result4 = invoke_memfun(&X::foo, x, 1, 2);

  std::cout << result1 << std::endl;
  std::cout << result2 << std::endl;
  std::cout << result3 << std::endl;
  std::cout << result4 << std::endl;
}

出力

3
3
Hello World
3

バージョン

言語

  • C++11

処理系

  • Clang: 3.0
  • GCC, C++11 mode: 4.6.4
  • Visual C++: 9.0 (std::tr1), 10.0, 11.0, 12.0, 14.0
    • 9.0~10.0は、TR1に基づく実装である。decltypeを使用せず、関数オブジェクトに定義されたresult_typeが使用される。
    • 11.0までは、可変引数テンプレートに対応していないため、不完全な実装である。
    • result_of_tは、12.0から。

参照