Потенциально связанные статьи:Почему аргумент ссылки rvalue соответствует заданию const в разрешении перегрузки?
Для STL контейнера C
, std::begin(C)
и аналогичные функции доступа, включая std::data(C)
(так как C++, 17), как предполагается, имеют такое же поведение от C::begin()
и других соответствующих методов C
. Тем не менее, я наблюдаю некоторое интересное поведение из-за деталей разрешения перегрузки с использованием ссылок lvalue/rvalue и константы.
DataType1
является int*
как легко ожидалось. Также подтвердили с помощью Boost's type_id_with_cvr
. const vector<int>
дает int const*
Здесь нет ничего удивительного.
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.match). Было бы намного яснее, если бы кто-то мог указать точные правила для этой проблемы.
Интересно. Для 'begin' /' end' это обычно не замечается, так как вы никогда не будете использовать их на rvalue. Вы скажете 'auto && __x = f(); auto it = begin (__ x); ', поэтому аргумент всегда является lvalue. С помощью 'data' можно было бы аналогичным образом утверждать, что данные без размера бесполезны, поэтому вам нужно также промежуточное значение lvalue. –
Но, может быть, вы все равно должны подать библиотеку. –