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

У меня есть некоторые проблемы с пониманием необходимости 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 ответа

Решение

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;
    };

Если вам нужен тип чего-то, что не похоже на вызов функции, 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...)> строго выше. Это яснее, короче и правильно поддерживает больше F с .


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

с исключениями. Хотя он поддерживает указатели на членов, 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,

Другие вопросы по тегам