Ответ 1
std::result_of<T>
требует, чтобы T
был типом - но не только любым типом. T
должен быть типом функции, поэтому эта частичная специализация result_of
будет использоваться:
template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;
такое, что:
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...))
хорошо сформирован (С++ 11 20.9.7.6). (INVOKE определяется в 20.8.2.)
Причина std::result_of<f(int)>
не работает, потому что f
не является типом - это экземпляр типа функции. Чтобы объявить x
как возвращаемый тип f
, примененный к int
, просто напишите это:
decltype(f(int{})) x;
или если вы предпочитаете жесткое кодирование int
:
decltype(f(32)) x;
Если нужен тип f
, используйте:
using FuncPtr = decltype(f);
В предоставленном коде f
(т.е. не в нижнем регистре f
), однако, является типом, и поэтому F(int)
определяет тип, представляющий функцию, которая возвращает f
принятие int
в качестве аргумента. Ясно, что это не то, что F! f
type - это структура, экземпляры которой могут использовать оператор вызова функции. f
также не имеет явных или неявных конструкторов, принимающих int
и т.д. Как это может работать? Короткий ответ: Шаблон "магия".
По существу, определение std::result_of
принимает тип F(int)
и отделяет возвращаемый тип от типов аргументов, чтобы он мог определить, какой случай INVOKE() позволит ему работать. Случаями INVOKE являются:
- F является указателем на функцию-член для некоторого класса T
- Если есть только один аргумент, F является указателем на элемент данных класса T или
- Экземпляр F может использоваться как функция, т.е.
declval<F>()(declval<int>())
который может быть нормальным вызовом функции или некоторым типом функтора (например, как ваш пример).
Как только это определено, result_of
может затем определить возвращаемый тип действительного выражения. Это то, что возвращается через член result_of
type
.
Прекрасная вещь в том, что пользователь result_of
не должен знать ничего о том, как это работает. Единственное, что нужно понять, - это то, что result_of
нужна функция TYPE. Если вы используете имена, которые не являются типами внутри кода (например, f
), то decltype
нужно будет использовать для получения типа выражения с таким.
Наконец, часть причины, по которой f
не может рассматриваться как тип, состоит в том, что параметры шаблона также допускают постоянные значения, а f
- значение указателя постоянной функции. Это легко продемонстрировать (используя определение вопроса f
):
template <double Op(int)>
double invoke_op(int i)
{
return Op(i);
}
и позже:
std::cout << invoke_op<f>(10) << std::endl;
Итак, чтобы получить тип возвращаемого значения выражения, правильно вызывающего f
с некоторым int
, можно было бы написать:
decltype(f(int{}))
(Примечание: f
никогда не вызывается: компилятор просто использует выражение внутри decltype
, чтобы определить его результат, то есть его возвращаемое значение в этом экземпляре.)