Разница между 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
.