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