Supongo que no, pero me gustaría confirmarlo. ¿Tiene algún uso const Foo&&
dónde Foo
está un tipo de clase?
c++
c++11
constants
rvalue-reference
fredoverflow
fuente
fuente
const&&
son muy importantes, aunque no dice por qué: youtube.com/watch?v=JhgWFYfdIho#t=54m20sRespuestas:
En ocasiones son útiles. El propio borrador de C ++ 0x los usa en algunos lugares, por ejemplo:
template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete;
Las dos sobrecargas anteriores aseguran que las otras funciones
ref(T&)
ycref(const T&)
no se unan a rvalues (que de otra manera sería posible).Actualizar
Acabo de verificar el estándar oficial N3290 , que desafortunadamente no está disponible públicamente, y tiene en 20.8 objetos de función [function.objects] / p2:
template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete;
Luego verifiqué el borrador más reciente posterior a C ++ 11, que está disponible públicamente, N3485 , y en 20.8 Objetos de función [function.objects] / p2 todavía dice:
template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete;
fuente
const T&&
utiliza algún otro lugar ?const T&&
evita que alguien use tontamente los argumentos de plantilla explícitos del formularioref<const A&>(...)
. Ese no es un argumento muy fuerte, pero el costo deconst T&&
másT&&
es bastante mínimo.La semántica de obtener una referencia const rvalue (y no para
=delete
) es para decir:El siguiente caso de uso podría haber sido en mi humilde opinión un buen caso de uso para la referencia de rvalue a const , aunque el lenguaje decidió no adoptar este enfoque (ver la publicación SO original ).
El caso: constructor de punteros inteligentes a partir de puntero en bruto
Por lo general, sería aconsejable usar
make_unique
ymake_shared
, pero ambosunique_ptr
yshared_ptr
se pueden construir a partir de un puntero sin formato. Ambos constructores obtienen el puntero por valor y lo copian. Ambos permiten (es decir, en el sentido de: no previenen ) un uso continuo del puntero original que se les pasó en el constructor.El siguiente código se compila y da como resultado double free :
int* ptr = new int(9); std::unique_ptr<int> p { ptr }; // we forgot that ptr is already being managed delete ptr;
Ambos
unique_ptr
yshared_ptr
podrían evitar lo anterior si sus constructores relevantes esperaran obtener el puntero sin formato como un valor constante , por ejemplo, paraunique_ptr
:unique_ptr(T* const&& p) : ptr{p} {}
En cuyo caso, el código doble libre anterior no se compilaría, pero lo siguiente sí:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Tenga en cuenta que
ptr
aún se podría usar después de que se movió, por lo que el error potencial no desapareció por completo. Pero si el usuario debe llamar astd::move
un error de este tipo, caería en la regla común de: no use un recurso que se movió.Uno puede preguntar: OK, pero ¿por qué
T*
const&& p
?La razón es simple, para permitir la creación de un
unique_ptr
puntero constante . Recuerde que la referencia const rvalue es más genérica que solo la referencia rvalue, ya que acepta tantoconst
ynon-const
. Entonces podemos permitir lo siguiente:int* const ptr = new int(9); auto p = std::unique_ptr<int> { std::move(ptr) };
esto no funcionaría si esperáramos solo una referencia de rvalue (error de compilación: no se puede vincular const rvalue a rvalue ).
De todos modos, es demasiado tarde para proponer tal cosa. Pero esta idea presenta un uso razonable de una referencia rvalue a const .
fuente
Están permitidos e incluso las funciones se clasifican según
const
, pero como no puede moverse desde el objeto const referido porconst Foo&&
, no son útiles.fuente
const T&, T&, const T&&, T&&
Además de std :: ref , la biblioteca estándar también usa la referencia const rvalue en std :: as_const para el mismo propósito.
template <class T> void as_const(const T&&) = delete;
También se usa como valor de retorno en std :: opcional cuando se obtiene el valor envuelto:
constexpr const T&& operator*() const&&; constexpr const T&& value() const &&;
Así como en 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;
Esto es presumiblemente para mantener la categoría de valor así como la consistencia de la envoltura al acceder al valor envuelto.
Esto hace una diferencia si las funciones calificadas por ref const rvalue se pueden llamar en el objeto empaquetado. Dicho esto, no conozco ningún uso para las funciones calificadas const rvalue ref.
fuente
No puedo pensar en una situación en la que esto sea útil directamente, pero podría usarse indirectamente:
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); }
La T en g es T constante, entonces f 's x es una T constante &&.
Es probable que esto dé como resultado un error de comilla en f (cuando intenta mover o usar el objeto), pero f podría tomar un rvalue-ref para que no se pueda llamar en lvalues, sin modificar el rvalue (como en el demasiado simple ejemplo anterior).
fuente