Чи мають значення посилання rvalue на const?


Відповіді:


78

Вони час від часу корисні. Проект C ++ 0x сам використовує їх у кількох місцях, наприклад:

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

Зазначені дві перевантаження гарантують, що інші ref(T&)та cref(const T&)функції не прив’язуються до значень r (що в іншому випадку було б можливим).

Оновлення

Я щойно перевірив офіційний стандарт N3290 , який, на жаль, не є загальнодоступним, і він має в 20.8 об’єктах функції [function.objects] / p2:

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

Потім я перевірив останню версію пост-C ++ 11, яка є загальнодоступною, N3485 , і в 20.8 Функціональних об'єктах [function.objects] / p2 все одно написано:

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

Дивлячись на cppreference, виявляється, це вже не так. Будь-які ідеї чому? Будь-які інші місця const T&&використовуються?
Pubby

58
Чому ви тричі включили той самий код у свою відповідь? Я намагався знайти різницю занадто довго.
typ1232

9
@ typ1232: Здається, я оновив відповідь майже через 2 роки після того, як відповів на неї, через занепокоєння у коментарях щодо того, що посилаються функції більше не з'являлися. Я зробив копію / вставку з N3290 і з останнього на той час чернетки N3485, щоб показати, що функції все-таки з’явилися. На той час, на мою думку, використання copy / paste було найкращим способом переконатись, що більша кількість очей, ніж мої, могла підтвердити, що я не ігнорував деякі незначні зміни в цих підписах.
Говард Хіннант,

1
@kevinarpe: "Інші перевантаження" (не показано тут, але в стандарті) беруть посилання lvalue і не видаляються. Показані тут перевантаження краще підходять для rvalues, ніж ті перевантаження, які приймають посилання lvalue. Тож аргументи rvalue прив'язуються до перевантажень, показаних тут, а потім викликають помилку часу компіляції, оскільки ці перевантаження видаляються.
Говард Хіннант,

1
Використання const T&&заважає комусь безглуздо використовувати явні аргументи шаблону форми ref<const A&>(...). Це не дуже сильний аргумент, але вартість const T&&більш T&&досить мінімальна.
Говард Хіннант,

6

Семантика отримання посилання на const rvalue (а не для =delete) полягає в тому, щоб сказати:

  • ми не підтримуємо операцію для lvalues!
  • навіть незважаючи на те, що ми все одно копіюємо , оскільки ми не можемо перемістити переданий ресурс або тому, що немає фактичного значення для "переміщення" його.

Наступний варіант використання міг би бути IMHO хорошим варіантом використання посилання rvalue на const , хоча мова вирішила не застосовувати цей підхід (див. Оригінальний пост SO ).


Справа: конструктор розумних покажчиків із вихідного вказівника

Зазвичай було б доцільно використовувати make_uniqueі make_shared, але обидва unique_ptrі shared_ptrможна побудувати з необробленого вказівника. Обидва конструктори отримують покажчик за значенням і копіюють його. Обидва дозволяють (тобто в сенсі: не перешкоджати ) продовження використання оригінального вказівника, переданого їм у конструкторі.

Наступний код компілюється та отримує результати з подвійним безкоштовним :

int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;

І те, unique_ptrі іншеshared_ptr можуть запобігти вищезазначеному, якщо їх відповідні конструктори очікують отримати вихідний покажчик як значення const , наприклад для unique_ptr:

unique_ptr(T* const&& p) : ptr{p} {}

У цьому випадку подвійний безкоштовний код, наведений вище, не компілюється, але наступне:

std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) };     // ok, rvalue

Зверніть увагу, що його ptrможна було використовувати після переміщення, тому потенційна помилка повністю не зникла. Але якщо користувачеві потрібно зателефонувати, std::moveтака помилка підпадає під загальне правило: не використовуйте переміщений ресурс.


Можна запитати: добре, але навіщо T* констирувати&& p ?

Причина проста, дозволити створення unique_ptr з вказівника const . Пам'ятайте, що посилання на const rvalue є більш загальним, ніж просто посилання на rvalue, оскільки воно приймає constі non-const. Тож ми можемо дозволити наступне:

int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };

це не пішло б, якби ми очікували лише посилання на rvalue (помилка компіляції: неможливо прив'язати const rvalue до rvalue ).


Так чи інакше, пізно пропонувати таке. Але ця ідея дійсно представляє розумне використання посилання rvalue на const .


Я був серед людей з подібними ідеями, але не мав підтримки для просування вперед.
Червона. Хвиля

"auto p = std :: unique_ptr {std :: move (ptr)};" не компілюється з помилкою "Помилка вирахування аргументу шаблону класу". Я думаю, це має бути "unique_ptr <int>".
Зехуй Лін,

4

Вони дозволені і навіть класифікуються на основі функцій const, але оскільки ви не можете перейти від об'єкта const, на який посилається const Foo&&, вони не є корисними.


Що саме ви маєте на увазі під зауваженням "за рейтингом"? Я думаю, щось пов’язане з дозволом на перевантаження?
fredoverflow

Чому ви не можете перейти з const rvalue-ref, якщо даний тип має ctor переміщення, який приймає const rvalue-ref?
Fred Nurk,

6
@FredOverflow, рейтинг перевантажень такий:const T&, T&, const T&&, T&&
Ген Бушуєв

2
@Fred: Як ви рухаєтесь, не змінюючи джерело?
fredoverflow

3
@Fred: Змінні члени даних або, можливо, переміщення для цього гіпотетичного типу не вимагає зміни членів даних.
Fred Nurk,

2

Окрім std :: ref , стандартна бібліотека також використовує посилання const rvalue у std :: as_const з тією ж метою.

template <class T>
void as_const(const T&&) = delete;

Він також використовується як повернене значення в std :: optional (необов’язково) при отриманні загорнутого значення:

constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;

Як і в std :: get :

template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;

Це, мабуть, для того, щоб зберегти категорію значень, а також констентність обгортки при доступі до обгорнутого значення.

Це робить різницю в тому, чи можна викликати функції const rvalue, які мають відношення, на обгорнутому об’єкті. Тим не менш, я не знаю жодного використання для кваліфікованих функцій const rvalue ref.


1

Я не можу подумати про ситуацію, коли це могло б бути корисним безпосередньо, але це може бути використано опосередковано:

template<class T>
void f(T const &x) {
  cout << "lvalue";
}
template<class T>
void f(T &&x) {
  cout << "rvalue";
}

template<class T>
void g(T &x) {
  f(T());
}

template<class T>
void h(T const &x) {
  g(x);
}

T в g є T const, тому f 's x є T const &&.

Цілком ймовірно, це призводить до помилки компіляції у f (коли вона намагається перемістити або використати об'єкт), але f може взяти rvalue-ref, щоб його не можна було викликати на lvalues, не змінюючи rvalue (як у занадто простому приклад вище).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.