У меня проблемы с пониманием необходимости 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
том, что для первого требуется выражение, а для второго - нет?
decltype
уродливее, но и мощнее.result_of
может использоваться только для вызываемых типов и требует типов в качестве аргументов. Например, вы не можете использоватьresult_of
здесь:template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );
if аргументы могут быть арифметическими типами (нетF
такой функции , которую вы можете определитьF(T,U)
для представленияt+u
. Для пользовательских типов вы могли бы. Таким же образом (я действительно не играл с этим), я полагаю что вызовы методов-членов могут быть труднымиresult_of
без использования связующих или лямбдstd::declval
помощью кода, который я показал выше. Конечно, это некрасиво :)result_of
вспомогательный тип и его вспомогательный типresult_of_t
устарели в C ++ 17 в пользуinvoke_result
иinvoke_result_t
, что смягчает некоторые ограничения первого. Они перечислены внизу en.cppreference.com/w/cpp/types/result_of .Если вам нужен тип чего-то, что не похоже на вызов функции,
std::result_of
просто не применяется.decltype()
может дать вам тип любого выражения.Если мы ограничимся только различными способами определения типа возвращаемого значения вызова функции (между
std::result_of_t<F(Args...)>
иdecltype(std::declval<F>()(std::declval<Args>()...)
), тогда будет разница.std::result_of<F(Args...)
определяется как:В этом вся разница между
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
s ‡ .† Если, то есть, вы не используете его в контексте, в котором вы не хотите разрешать указатели на члены, он
std::result_of_t
будет успешным в случае, когда вы, возможно, захотите, чтобы он потерпел неудачу.‡ За исключениями. Хотя он поддерживает указатели на члены,
result_of
не будет работать, если вы попытаетесь создать экземпляр недопустимого идентификатора типа . Они могут включать функцию, возвращающую функцию или принимающую абстрактные типы по значению. Например:Правильное использование было бы правильным
result_of_t<F&()>
, но это деталь, о которой не нужно помнитьdecltype
.источник
T
и функции ?template<class F> result_of_t<F&&(T&&)> call(F&& f, T&& arg) { return std::forward<F>(f)(std::move(arg)); }
result_of_t
f
этоconst T
, мы должны использоватьresult_of_t<F&&(const T&)>
?