Разница между std :: result_of и decltype - PullRequest
96 голосов
/ 22 апреля 2010

У меня возникли проблемы с пониманием необходимости std::result_of в C ++ 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);
}

Единственная проблема, которую я вижу с этими двумя решениями, заключается в том, что нам нужно либо:

  • есть экземпляр функтора, чтобы использовать его в выражении, передаваемом в decltype.
  • знать определенный конструктор для функтора.

Правильно ли я считаю, что единственная разница между decltype и result_of заключается в том, что первый нуждается в выражении, а второй нет?

Ответы [ 2 ]

84 голосов
/ 22 апреля 2010

result_of был введен в Boost , а затем включен в 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;
    };
11 голосов
/ 07 марта 2016

Если вам нужен тип чего-то, что не похоже на вызов функции, 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>()...) заключается именно в этом INVOKE. Использование declval / decltype напрямую, в дополнение к тому, что он немного дольше печатается, допустимо только в том случае, если F напрямую вызывается (тип объекта функции или функция или указатель на функцию). result_of дополнительно поддерживает указатели на функции-члены и указатели на данные-члены.

Первоначально, использование declval / decltype гарантировало дружественное к SFINAE выражение, тогда как std::result_of может привести к серьезной ошибке вместо ошибки вывода. Это было исправлено в C ++ 14: std::result_of теперь требуется для SFINAE (благодаря этой статье ).

Так что на соответствующем компиляторе C ++ 14 std::result_of_t<F(Args...)> строго превосходит. Он четче, короче и правильно & dagger; поддерживает больше F s & Dagger; .

<ч /> & dagger; Если вы не используете его в контексте, в котором вы не хотите разрешать указатели для членов, то std::result_of_t будет успешным в случае, если вы захотите, чтобы он потерпел неудачу.

& Dagger; С исключениями. Хотя он поддерживает указатели на члены, result_of не будет работать, если вы попытаетесь создать недействительный type-id . Они будут включать функцию, возвращающую функцию или принимающую абстрактные типы по значению. Ex.:

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.

...