24
#include <iostream>
#include <type_traits>

double f(int i)
{
        return i+0.1;
}

struct F
{
        public:
        double operator ()(int i) { return i+0.1; }
};

int
main(int, char**)
{
        std::result_of<F(int)>::type x;     // ok
        // std::result_of<f(int)>::type x; // error: template argument 1 is invalid
        x = 0.1;
        std::cerr << x << std::endl;
}

std::result_of<f(int)>::type x;無効な理由を教えてください...

cppreference は、「( std::result_of) コンパイル タイプで関数呼び出し式の戻り値の型を推測する」と述べています。

どうしたの?

4

1 に答える 1

29

std::result_of<T>タイプである必要Tがありますが、任意のタイプではありません。Tは関数型でなければならないため、この部分的な特殊化result_ofが使用されます。

template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;

そのような:

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

整形式です (C++11 20.9.7.6)。(INVOKE は 20.8.2 で定義されています。)

std::result_of<f(int)>機能しない理由fは、型ではないためです。関数型のインスタンスです。an に適用されたxの戻り値の型であることを宣言するには、次のように記述します。fint

decltype(f(int{})) x;

または、ハードコーディングを好む場合int:

decltype(f(32)) x;

のタイプfが必要な場合は、次を使用します。

using FuncPtr = decltype(f);

ただし、提供されたコードF(つまり、小文字ではないf) は型であるため、 を引数として受け入れるF(int)関数を表す型を定義します。明らかに、これは F とは異なります。の型は、インスタンスが関数呼び出し演算子を使用できる構造体です。などを取る明示的または暗黙的なコンストラクターもありません。これはどのように機能しますか?簡単な答え: テンプレート「魔法」。FintFFint

基本的に、 の定義は型をstd::result_of取り、F(int)戻り値の型を引数の型から分離するので、INVOKE() のどのケースが機能するかを判断できます。INVOKE のケースは次のとおりです。

  1. F は、あるクラス T のメンバー関数へのポインターです。
  2. 引数が 1 つしかない場合、F はクラス T のデータ メンバーへのポインター、または
  3. F のインスタンスは、関数として使用できます。つまり、
declval<F>()(declval<int>())

これは、通常の関数呼び出しまたは何らかのタイプのファンクター (たとえば、例のように) にすることができます。

これが決定されるresult_ofと、有効な式の戻り値の型を決定できます。これは、result_oftypeメンバーを介して返されるものです。

これの素晴らしいところは、 のユーザーがresult_ofこれが実際にどのように機能するかについて何も知る必要がないことです。理解しなければならない唯一のことはresult_of、関数 TYPE が必要なことです。コード内で型ではない名前を使用している場合 (例: f)、そのdecltypeような式の型を取得するために使用する必要があります。

f最後に、が型と見なされない理由の一部は、テンプレート パラメーターも定数値を許可fし、定数関数ポインター値であるためです。これは簡単に実証できます (質問の の定義を使用f):

template <double Op(int)>
double invoke_op(int i)
{
  return Op(i);
}

以降:

std::cout << invoke_op<f>(10) << std::endl;

したがって、式の戻り値の型を適切に取得するには、次のように記述fします。int

decltype(f(int{}))

(注:fが呼び出されることはありません。コンパイラは単に内部の式を使用して、decltypeその結果、つまりこのインスタンスの戻り値を決定します。)

于 2012-07-13T14:14:48.483 に答える