Думаю, нет, но хочу подтвердить. Есть ли смысл const Foo&&
, где Foo
тип класса?
Думаю, нет, но хочу подтвердить. Есть ли смысл const Foo&&
, где Foo
тип класса?
Ответы:
Иногда они полезны. Сам черновик C ++ 0x использует их в нескольких местах, например:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Выше двух перегрузок гарантировать , что другие ref(T&)
и cref(const T&)
функцию не связываются с rvalues (что было бы возможно).
Обновить
Я только что проверил официальный стандарт 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 [function.objects] / p2 он все еще говорит:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
const T&&
используются?
const T&&
предотвращает глупое использование явных аргументов шаблона формы ref<const A&>(...)
. Это не очень веский аргумент, но цена const T&&
за T&&
него минимальна.
Семантика получения ссылки на const rvalue (а не for =delete
) заключается в том, чтобы сказать:
Следующий вариант использования мог бы быть 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 rvalue , например, для 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*
const&& p
?
Причина проста - разрешить создание unique_ptr
из константного указателя . Помните, что ссылка 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 .
Они разрешены, и даже функции ранжируются на основе const
, но поскольку вы не можете перейти от объекта const, на который ссылается const Foo&&
, они бесполезны.
const T&, T&, const T&&, T&&
Помимо 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 ref. Тем не менее, я не знаю, как использовать функции с квалификацией const rvalue ref.
Я не могу представить себе ситуацию, когда это было бы полезно напрямую, но это могло бы использоваться косвенно:
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 x - это T const &&.
Вероятно, это приводит к ошибке компиляции в f (когда он пытается переместить или использовать объект), но f может принимать rvalue-ref, чтобы его нельзя было вызвать для lvalues без изменения rvalue (как в слишком простом пример выше).
const&&
они очень важны, хотя он не говорит почему: youtube.com/watch?v=JhgWFYfdIho#t=54m20s