С++ 11: аргумент шаблона std::result_of‹› против std::function‹›

Я провел некоторое исследование std::result_of<> и знаю, как он используется на высоком уровне, но я все еще довольно запутался в этой волшебной вещи.

Итак, если я правильно понимаю, форма R (Args..) должна интерпретироваться компилятором как тип функции, принимающий аргументы типов Args... и возвращающий тип R, и использование очень оправдано при указании std::function<int(int)>, который объявляет тип функции, принимающий int в качестве единственного аргумента и возвращает int.

Однако значение вышеизложенного внезапно меняется, когда то же самое понятие используется в контексте std::result_of<F(Args...)> (обратите внимание, что я изменил R на F и удалил (space) between F и (, чтобы обозначить разницу - согласно cppreference, значение F(Args...) становится время компиляции INVOKE выражение - я имею в виду, я как бы понимаю, что основная цель std::result_of<F(Args...)> состоит в том, чтобы использовать понятие INVOKE для выбора правильной перегрузки (если присутствует более одной перегрузки) и, конечно же, вывести возвращаемый тип такого вызова (во время разрешения компиляции) - фактически это будет примерно похоже на: decltype(std::declval<F>()(std::declval<Args>()...)) - однако понятие F (Args...) - по сравнению с случаем std::function<R (Args...)> - будет интерпретировано компилятором как тип функции, принимающий Args... и возвращающий тип F, где теперь F — это тип функции, возвращаемый тип которой мы пытаемся получить!

Конечно, с помощью decltype и std::declval реализация std::result_of кажется разумной, но я читал, что boost каким-то образом реализовал result_of в C++98/03, где decltype и std::declval не существовало - мне интересно, как это возможно (может быть, с некоторыми очень хакерскими трюками)?

Итак, еще раз: является ли понятие R (Args...) в контексте того, что аргумент шаблона всегда интерпретируется/выводится как тип функции с типом возвращаемого значения R, независимо от того, является ли закрывающий шаблон std::function<> или std::result_of<>? в то время как std::result_of<> каким-то образом переосмысливает «тип возвращаемого значения» F и «возвращает» фактический тип возвращаемого значения, определенный std::result_of<F(Args...)>::type? Или std::result_of<F(Args...)> просто интерпретирует F(Args...) по-другому, поэтому знает, что это магическое INVOKE выражение?

Спасибо за ваше предстоящее разъяснение!


person Dejavu    schedule 13.06.2018    source источник
comment
std::result_of просто злоупотребляет синтаксисом типа функции, и, как говорится в в этом примечании , Это не всегда работает.   -  person cpplearner    schedule 13.06.2018


Ответы (2)


Z(A,B,C) - это просто тип. Это тип функции.

Вы можете передавать типы в шаблоны. То, что шаблон делает с типом, зависит от шаблона.

std::function<X> ожидает тип функции. Он преобразует его в подпись своего operator().

std::result_of<X> ожидает тип функции. Если задан A(Ts...), он вычисляет результат использования () для вызова объекта типа A с аргументами Ts....

Он был помечен как устаревший в c++ 17, потому что типы функций иногда делают забавные вещи (аргументы затухания, удаление константы и т. д.). Теперь есть invoke_result<F, Ts...>, который ведет себя так же, как result_of<F(Ts...)>, с некоторыми отличиями в регистре.

Результатом всегда было злоупотребление синтаксисом типа функции. Но здесь нет глубокой магии; шаблоны делают с переданными типами все, что хотят.

В c++03 вы можете получить ограниченную функциональность, такую ​​как "result_of", путем сопоставления типов указателей функций и изучения &T::operator(). Невозможно получить все необходимые возможности в c++03 без decltype/declval, расширений компилятора или TR pre-c++11 заголовки.

person Yakk - Adam Nevraumont    schedule 13.06.2018
comment
Спасибо, но не могли бы вы подробнее рассказать о том, как реализовать result_of без использования decltype? - person Dejavu; 13.06.2018
comment
Кроме того, вы говорите, что Z(A,B,C) - это тип функции - хотя, если быть более точным, всегда ли он интерпретируется компилятором как тип функции, принимающий аргументы типа A, B, C соответственно, и возвращающий тип Z, или это понятие может быть интерпретировано по-другому по разным шаблонам? например std::function<> будет интерпретировать Z как тип возвращаемого значения, а std::result_of<> интерпретирует Z как сам тип функции?? Было бы здорово, если бы вы могли подробнее рассказать об этом. - person Dejavu; 13.06.2018
comment
@Dejavu синтаксически это функция, принимающая A, B, C и возвращающая F, но когда это параметры шаблона, нет никакой функции с такой сигнатурой. Это способ вставки нескольких параметров шаблона из частичной специализации в один параметр основного шаблона. - person Caleth; 13.06.2018
comment
Итак, std::result_of с самого начала был своего рода хаком? - person Dejavu; 13.06.2018
comment
@dejavu Я бы сказал, слишком умный, а не взлом. - person Yakk - Adam Nevraumont; 13.06.2018

Чтобы реализовать это без decltype — ну, простая реализация старой школы каркаса, которая охватывает несколько основных случаев, вероятно, будет чем-то вроде

template<class F> struct MemberRetVal;
template<class C,typename R> struct MemberRetVal<R (C::*)()> {
    using type = R;
};
template<class C,typename R> struct MemberRetVal<R (C::*)() const> {
    using type = R;
};
template<class C,typename R, typename A1> struct MemberRetVal<R (C::*)(A1)> {
    using type = R;
};
template<class C,typename R, typename A1> struct MemberRetVal<R (C::*)(A1) const> {
    using type = R;
};
// ...
// A few more member function arities
// ...

template<typename F> struct ResultOf;
template<typename F> struct ResultOf<F (*)()> {
    using type = typename MemberRetVal<&F::operator()>::type;
};
template<typename R> struct ResultOf<(R (*)())(*)()> {
    using type = R;
};
template<typename F, typename A1> struct ResultOf<F (*)(A1)> {
    using type = typename MemberRetVal<&F::operator()>::type;
};
template<typename R, typename A1> struct ResultOf<(R (*)(A1))(*)(A1)> {
    using type = R;
};
// and so forth

Для случая operator() требуется несколько более изощренная диспетчеризация, но в остальном — это приблизительный путь.

person bipll    schedule 13.06.2018
comment
Спасибо, но мне не удалось скомпилировать ваш код с помощью простого примера, не могли бы вы как-то сделать этот рабочий пример на Corliru? Спасибо! - person Dejavu; 15.06.2018