Почему аргумент ссылки rvalue соответствует ссылке const в разрешении перегрузки?


Потенциально связанные статьи:

Для контейнера STLC, std::begin(C) и аналогичные функции доступа, включая std::data(C) (начиная с C++17), должны иметь такое же поведение C::begin() и других соответствующих методов C. Тем не менее, я наблюдаю некоторые интересные поведения Из-за деталей разрешения перегрузки, включая ссылки и константы lvalue/rvalue.

DataType1 есть int* как и следовало ожидать. Кроме того, подтверждена связь с Boost'S type_id_with_cvr. const vector<int> не вызывает здесь никакого удивления.

using T = vector<int>;
using DataType1 = decltype(T().data()); // int*
using CT = const T;
using DataType2 = decltype(CT().data()); // int const*

using boost::typeindex::type_id_with_cvr;
cout << type_id_with_cvr<DataType1>() << endl; // prints int*
...

Я попробовал std::data, который также может обрабатывать массивы и контейнеры без STL. Но он уступает int const*. Аналогично, std::beginвозвращает итератор const, хотя T не является const.

using T = vector<int>;
using DataType3 = decltype(std::data(T())); // int const* Why ???
using CT = const T;
using DataType4 = decltype(std::data(CT())); // int const*

Вопрос : в чем причина этого различия? Я ожидал, что C.data() и std::data(C) вел бы себя точно так же.


Некоторые мои исследования: чтобы получить int* для DataType3, T должен быть явно преобразован в неконстантный ссылочный тип lvalue. Я попробовал declval, и это сработало.

using DataType3 = decltype(std::data(std::declval<T&>())); // int*

std::data обеспечивает две перегрузки:

template <class _Cont> constexpr
auto data(_Cont& __c) -> decltype(__c.data()) { return __c.data(); }

// non-const rvalue reference argument matches to this version.
template <class _Cont> constexpr
auto data(const _Cont& __c) -> decltype(__c.data()) { return __c.data(); }

При разрешении перегруженных функций для std::data, T(), который является неконстантной ссылкой rvalue, соответствует версии const T& вместо версии T&.

Найти это было нелегко специфическое правило разрешения перегрузки в стандарте (13.3, over.спичка). Было бы гораздо яснее, если бы кто-то мог указать точные правила для этого вопроса.

2 3

2 ответа:

Это поведение приписывается правилам разрешения перегрузки. Согласно стандарту 8.5.3 / p5.2 ссылки [dcl.в этом.ref] , ссылки rvalue привязываются к ссылкам const lvalue. В этом примере:

std::data(T())

Вы предоставляете std::data Значение rvalue. Таким образом, из-за разрешения перегрузки правила перегрузки:

template <class _Cont> constexpr
auto data(const _Cont& __c) -> decltype(__c.data()) { return __c.data(); }

- это лучшая партия. Следовательно, вы получаете const int*

Вы не можете привязать временное значение к неконстантной ссылке lvalue.

Единственная строка, которая вызывает легкое удивление, - это using DataType1 = decltype(T().data()); // int*.

...но это все еще нормально, член функции могут быть вызваны на временных объектах, не рассматриваясь как const. Это еще одно нетривиальное различие между функциями-членами и свободными функциями.

Например, в C++98 (pre-rvalue refs) было невозможно сделать std::ofstream("file.txt") << std::string("text"), потому что operator<< не является членом, а временное рассматривается как const. Если бы operator<< был членом std::ofstream, это было бы возможно. (и может даже иметь смысл). (Ситуация изменилась позже в C++11 со ссылками rvalue, но точка все еще действительна).

Вот пример:

#include<iostream>
struct A{
    void f() const{std::cout << "const member" << std::endl;}
    void f(){std::cout << "non const member" << std::endl;}
};

void f(A const&){std::cout << "const function" << std::endl;}
void f(A&){std::cout << "non const function" << std::endl;}

int main(){
    A().f(); // prints "non const member"
    f(A()); // prints "const function"
}
Поведение проявляется, когда объект временно сконструирован и используется. Во всех других случаях, которые я могу себе представить, член f эквивалентен свободной функции f. (r-value эталонные квалификации && - для члена и функции-могут дать вам более точный контроль, но это не было частью вопроса.)