Разница между std:: result_of и decltype

У меня есть некоторые проблемы с пониманием необходимости std::result_of в С++ 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 заключается в том, что первому нужно выражение, а второе - не?

Ответ 1

result_of был представлен в Boost, а затем включенный в TR1 и, наконец, в С++ 0x. Поэтому result_of имеет преимущество, совместимое с обратной связью (с подходящей библиотекой).

decltype - совершенно новая вещь в С++ 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;
    };

Ответ 2

Если вам нужен тип чего-то, что не похоже на вызов функции, 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 могло бы дать вам жесткую ошибку вместо отказа дедукции. Это исправлено в С++ 14: std::result_of теперь требуется быть SFINAE-дружественным (благодаря этой статье).

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


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

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

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.