Думаю, нет, но хочу подтвердить. Есть ли смысл 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