TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #395 4.37K
​​Примеры использования const T&&
#опытным

В прошлый раз мы поговорили о том, что можно использовать константную правую ссылку для того, чтобы запретить принимать любые rvalue reference в функцию.

Для чего это может быть нужно?

Допустим, мы храним в поле класса в каком-то виде ссылку на объект. И нам бы очень не хотелось принимать в конструкторе rvalue reference, потому что возможно сразу же после выхода из конструктора для объектов вызовется деструктор и хана этим объектам. И встречаем UB из-за хранения битой ссылки.

Есть такой стандартный класс std::reference_wrapper и его функции помощники std::ref() и std::cref(). Поскольку std::reference_wrapper предполагает хранение ссылки только для lvalue, то стандарт удалил перегрузки std::ref() и std::cref(), которые принимают const T&&.

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


По той же самой причине такая перегрузка удалена у функции std::as_const, которая формирует левую ссылку на константный тип из аргумента.

template< class T >  
constexpr std::add_const_t<T>& as_const( T& t ) noexcept;


Также константные правые ссылки используются в более сложных штуках, типа std::optional, когда нужно вернуть из него значение.

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


С этой же целью оно используется, например, и в std::get.

template< std::size_t I, class... Types >  
constexpr const std::variant_alternative_t<I, std::variant<Types...>>&&
    get( const std::variant<Types...>&& v );

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


В таких случаях использование const T&& оправдано передачей информации и о ссылочности типа, и о его константности. Это важно в обобщенном программировании, потому что никто не знает с каким типом будет работать шаблонная сущность. Вы вполне можете получить константный временный объект std::optional(да и любого другого объекта), это синтаксически корректно. И чтобы геттер его внутреннего значение отражал свойства обертки, приходится перегружать эти геттеры для любых возможных параметров. Так вот например методы std::optional упомянутые выше вызовутся только для временных константных объектов. И эти свойства отображаются в возвращаемом значении.

Также не стоит забывать, что константность объекта не накладывает ультимативных ограничений на использование объекта. Есть мутабельные и статические поля, которые можно изменять, и плевать они хотели на вашу константность. А также указатели. Мы не можем менять сам указатель, но можем изменить объект, на который он указывает. Это немного расширяет спектр возможностей использования константных правых ссылок, но не прям существенно. В голову пришел очевидный пример - pimpl idiom. Согласно этой идиоме класс хранит указатель на реализацию, в которой может лежать все, что угодно. Все операции, которые как-то изменяют состояние объекта, влияют на данные внутри указателя. Поэтому снаружи кажется, что объект и не изменился. Да и старый объект можно будет использовать. Непонятно только, зачем менять привычные традиции использования правых ссылок, но все же.

// MyClass.hpp
class MyClass {
public:
MyClass();
MyClass(int g_meat);
MyClass(const MyClass &&other); // const rvalue reference!
~MyClass();
int GetMeat() const;
private:
class Pimpl;
Pimpl *impl {};
};

// MyClass.cpp
class MyClass::Pimpl {
public:
int meat {42};
};

MyClass::MyClass() : impl {new Pimpl} { }

MyClass::MyClass(int g_meat) : MyClass() {
impl->meat = g_meat;
}

MyClass::MyClass(const MyClass &&other) : MyClass()
{
impl->meat = other.impl->meat;
other.impl->meat = 0;
}

MyClass::~MyClass() { delete impl; }

int MyClass::GetMeat() const {
return impl->meat;
}

// main.cpp
int main() {
const MyClass a {100500};
MyClass b (std::move(a)); // moving from const!
std::cout << a.GetMeat() << "\n"; // returns 0, b/c a is moved-from
std::cout << b.GetMeat() << "\n"; // returns 100500
}


Stay useful even if nobody understands you. Stay cool.

#template #cpp11 #STL
  • ❤ 11
  • 👍 8
  • 🔥 5
  • ⚡ 3
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →