Почему аргумент ссылки 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 ответа:
Это поведение приписывается правилам разрешения перегрузки. Согласно стандарту 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
эталонные квалификации&&
- для члена и функции-могут дать вам более точный контроль, но это не было частью вопроса.)