Подтвердить что ты не робот

Std:: result_of простая функция

#include <iostream>
#include <type_traits>

double f(int i)
{
        return i+0.1;
}

struct F
{
        public:
        double operator ()(int i) { return i+0.1; }
};

int
main(int, char**)
{
        std::result_of<F(int)>::type x;     // ok
        // std::result_of<f(int)>::type x; // error: template argument 1 is invalid
        x = 0.1;
        std::cerr << x << std::endl;
}

Пожалуйста, объясните, почему std::result_of<f(int)>::type x; недействителен...

cppreference says "(std::result_of) Устраняет тип возвращаемого выражения выражения функции при компиляции типа.".

какая проблема?

4b9b3361

Ответ 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, чтобы определить его результат, то есть его возвращаемое значение в этом экземпляре.)