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

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, чтобы он также отбрасывал эти квалификаторы.

4b9b3361

Ответ 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 являются ссылками.