Result_of для объекта-члена с аргументом cv-qualified
Учитывая следующие объявления:
struct MyClass { };
typedef int MyClass::*Mp;
В обоих компиляторах gcc 6.2 и Clang я попытался, result_of<Mp(const MyClass)>::type
дает int&&
.
Резюме моего вопроса: Почему int&&
, а не const int&&
или просто int
?
Больше фона: в стандарте указано, что result_of
определяется следующим образом:
тип typedef-члена должен указывать тип decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...));
Стандарт также определяет INVOKE для объектов-указателей-членов:
- t1. * f, когда N == 1 и f - указатель на элемент данных класса T и is_base_of_v<T, decay_t<decltype(t1)>>
- true;
Обратите внимание, что decay_t
предназначен только для проверки того, применяется ли эта марка. Насколько я могу судить, применение двух вышеперечисленных пунктов должно дать:
decltype(declval<const MyClass>().*declval<Mp>())
Что дает const int&&
. Итак, я что-то упустил или неправильно компилирует библиотеки компилятора?
Изменить, 30 августа 2016 года:
Спасибо за ответы. Несколько человек предложили альтернативные способы получения правильного результата без использования result_of
. Я должен пояснить, что причина, по которой я повесил правильное определение result_of
, заключается в том, что я фактически реализую ближайшую разумную реализацию result_of
, которая работает с компилятором pre-С++ 11. Поэтому, хотя я согласен с тем, что я могу использовать decltype
или result_of<Mp(const MyClass&&)>::type
в С++ 11, они не делают то, что мне нужно для С++ 03. Несколько человек дали правильный ответ, который заключается в том, что аргументы const rvalue для функций не являются частью типа функции. Это проясняет мне все, и я реализую свой pre-С++ 11 result_of
, чтобы он также отбрасывал эти квалификаторы.
Ответы
Ответ 1
const
лишается функциональных параметров. Вы можете проверить это, используя is_same
.
void(int) == void(const int)
Mp(MyClass) == Mp(const MyClass)
result_of<Mp(MyClass)> == result_of<Mp(const MyClass)>
Я думаю, что это объясняется [8.3.5.5]
:
После создания списка типов параметров любой верхний уровень cv-квалификаторы, изменяющие тип параметра, удаляются при формировании тип функции. Полученный список преобразованных типов параметров и наличие или отсутствие эллипсиса или пакета параметров функции это функция parameter-type-list. [Примечание: Это преобразование не влияет на типы параметров. Например, int(*)(const
int p, decltype(p)*)
и int(*)(int, const int*)
являются идентичными типами. - конечная нота]
Вы можете обойти это, указав свой собственный result_of
, который не использует (неправильно) типы функций:
template <typename F, typename... ArgTypes>
struct my_result_of
{
using type = decltype(std::invoke(std::declval<F>(), std::declval<ArgTypes>()...));
};
Это определение действительно то, что должен был использовать стандарт.
Ответ 2
В result_of_t<Mp(const MyClass)>
вы пытаетесь спросить, каков тип результата вызова Mp
с const
rvalue типа MyClass
. Лучше спросить, что с result_of
будет result_of_t<Mp(const MyClass&&)>
, но обычно проще использовать decltype
и забыть, что result_of
когда-либо существовал. Если вы намеревались спросить результат с const
lvalue, то это будет result_of_t<Mp(const MyClass&)>
.
Верно, что верхний уровень const
по функциональным параметрам не имеет смысла в объявлении функции. Поэтому при использовании result_of
имеет смысл указывать типы аргументов в качестве ссылок на возможные типы const
. Это также делает категорию значений явной, без потери выразительности. Мы можем использовать трюк print_type
, чтобы узнать, что произойдет, когда мы это сделаем:
template <typename...> struct print_type; // forward declaration
print_type<std::result_of_t<Mp(const MyClass)>,
std::result_of_t<Mp(const MyClass&)>,
std::result_of_t<Mp(const MyClass&&)>,
std::result_of_t<Mp(MyClass)>,
std::result_of_t<Mp(MyClass&)>,
std::result_of_t<Mp(MyClass&&)>>{};
Отпечатки:
error: invalid use of incomplete type 'struct print_type<int&&, const int&, const int&&, int&&, int&, int&&>'
Итак, мы можем вывести:
std::result_of_t<Mp(const MyClass)> == int&&
std::result_of_t<Mp(const MyClass&)> == const int&
std::result_of_t<Mp(const MyClass&&)> == const int&&
std::result_of_t<Mp(MyClass)> == int&&
std::result_of_t<Mp(MyClass&)> == int&
std::result_of_t<Mp(MyClass&&)> == int&&
Мы видим, что result_of_t<Mp(const MyClass)>
, result_of_t<Mp(MyClass)>
и result_of_t<Mp(MyClass&&)>
все означают одно и то же. Мне было бы удивительно, если бы они этого не сделали.
Обратите внимание, что при использовании declval
вы также предоставляете типы аргументов в качестве ссылок, поскольку объявляется declval
для возврата ссылки. Кроме того, все параметры std::invoke
являются ссылками.