std :: result_ofとdecltypeの違い


100

std::result_ofC ++ 0xでの必要性を理解するのに問題があります。私が正しく理解した場合result_ofは、特定のタイプのパラメーターを持つ関数オブジェクトを呼び出す結果のタイプを取得するために使用されます。例えば:

template <typename F, typename Arg>
typename std::result_of<F(Arg)>::type
invoke(F f, Arg a)
{
    return f(a);
}

次のコードとの違いは本当にわかりません。

template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(f(a)) //uses the f parameter
{
    return f(a);
}

または

template <typename F, typename Arg>
auto invoke(F f, Arg a) -> decltype(F()(a)); //"constructs" an F
{
    return f(a);
}

これら2つのソリューションで私が目にできる唯一の問題は、次のいずれかを行う必要があることです。

  • decltypeに渡される式で使用するファンクタのインスタンスがあります。
  • ファンクタの定義済みコンストラクタを知っています。

唯一の違いと考え中アムIの権利decltypeとは、result_of第二にはないのに対し、最初の1が式を必要とするということですか?

回答:


86

result_ofBoost導入され、次にTR1組み込まれ、最後にC ++ 0xに組み込まれました。したがってresult_of、(適切なライブラリを使用して)下位互換性があるという利点があります。

decltype C ++ 0xのまったく新しいものであり、関数の戻り値の型だけに限定されず、言語機能です。


とにかく、gcc 4.5ではresult_of、次の点で実装されていますdecltype

  template<typename _Signature>
    class result_of;

  template<typename _Functor, typename... _ArgTypes>
    struct result_of<_Functor(_ArgTypes...)>
    {
      typedef
        decltype( std::declval<_Functor>()(std::declval<_ArgTypes>()...) )
        type;
    };

4
私が理解する限りでdecltypeは、醜いだけでなく、より強力です。result_of呼び出し可能な型にのみ使用でき、引数として型が必要です。たとえば、result_ofここでは使用できません:template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );引数が算術型である可能性がある場合(を表すFために定義できる関数はありません。ユーザー定義型では可能です。同じように(私は実際に遊んだことがありません)そのメンバー・メソッドへの呼び出しを行うのは難しいかもしれない結合剤またはラムダを使用せずF(T,U)t+uresult_of
デビッド・ロドリゲス- dribeas

2
1つの注意点として、decltypeはAFAIKで関数を呼び出すために引数を必要とするため、result_of <>がないと、有効なデフォルトコンストラクターを持つ引数に依存せずに、テンプレートから返される型を取得するのは面倒です。
Robert Mason

3
@RobertMason:std::declval上記のコードのように、これらの引数はを使用して取得できます。もちろん、これは醜いです:)
kennytm

1
@DavidRodríguez-dribeasコメントに「(あり)」で始まる閉じられていない括弧があります:
Navin

1
別のノート:result_ofとそのヘルパーのタイプはresult_of_tC ++ 17の賛成で廃止されているinvoke_resultinvoke_result_t、元のいくつかの制限を緩和します。これらはen.cppreference.com/w/cpp/types/result_ofの下部にリストされています。
シグマ

11

関数呼び出しのようなものではないタイプのものが必要な場合std::result_ofは、適用されません。decltype()任意の式のタイプを与えることができます。

私たちは関数呼び出し(間の戻り値の型を決定するだけで、さまざまな方法に自分自身を制限する場合std::result_of_t<F(Args...)>decltype(std::declval<F>()(std::declval<Args>()...))、その差があります。

std::result_of<F(Args...) と定義されている:

INVOKE (declval<Fn>(), declval<ArgTypes>()...)評価されていないオペランドとして扱われるときに式 が適切に形成されている場合(条項5)、メンバーのtypedef型は型をdecltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...)); 別の名前で指定し、メンバーの型はありません。

違いresult_of<F(Args..)>::typeとは、decltype(std::declval<F>()(std::declval<Args>()...)すべてのことについてですINVOKEdeclval/をdecltype直接使用することは、タイプするのがかなり長くなるだけでなく、Fが直接呼び出すことができる場合にのみ有効です(関数オブジェクトタイプまたは関数または関数ポインター)。result_ofさらに、メンバー関数へのポインターとメンバーデータへのポインターをサポートします。

最初はdeclval/ を使用decltypeするとSFINAEに適した式が保証されましたstd::result_ofが、控除の失敗ではなくハードエラーが発生する場合がありました。これはC ++ 14で修正されました:std::result_ofSFINAEに対応する必要があります(このペーパーのおかげで)。

したがって、準拠するC ++ 14コンパイラでstd::result_of_t<F(Args...)>は、完全に優れています。より明確で短く、正しくはより多くF‡をサポートしています。


つまり、メンバーへのポインターを許可したくないコンテキストで使用してstd::result_of_tいる場合を除き、失敗したい場合に成功します。

例外あり。メンバーへのポインターをサポートしresult_ofていますが、無効なtype-idをインスタンス化しようとしても機能しません。これらには、関数を返す関数や、値によって抽象型を取得する関数が含まれます。例:

template <class F, class R = result_of_t<F()>>
R call(F& f) { return f(); }

int answer() { return 42; }

call(answer); // nope

正しい使い方はresult_of_t<F&()>でしたが、それはあなたが覚えておく必要のない詳細ですdecltype


非参照型Tと関数template<class F> result_of_t<F&&(T&&)> call(F&& f, T&& arg) { return std::forward<F>(f)(std::move(arg)); }の使用法はresult_of_t正しいですか?
Zizheng Tai、2016

また、我々はに渡す引数があればfであるconst T、我々は使うべきresult_of_t<F&&(const T&)>
Zizheng Tai、2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.