Acho que não, mas gostaria de confirmar. Existe alguma utilidade para const Foo&&
, onde Foo
está um tipo de classe?
c++
c++11
constants
rvalue-reference
fredoverflow
fonte
fonte
const&&
são muito importantes, embora não diga por quê: youtube.com/watch?v=JhgWFYfdIho#t=54m20sRespostas:
Eles são ocasionalmente úteis. O próprio rascunho C ++ 0x os usa em alguns lugares, por exemplo:
template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete;
As duas sobrecargas acima garantem que as outras funções
ref(T&)
ecref(const T&)
não se vinculem a rvalues (o que de outra forma seria possível).Atualizar
Acabei de verificar o padrão oficial N3290 , que infelizmente não está disponível publicamente e tem em 20.8 Objetos de função [function.objects] / p2:
template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete;
Então eu verifiquei o rascunho pós-C ++ 11 mais recente, que está disponível publicamente, N3485 , e em 20.8 Objetos de função [function.objects] / p2 ele ainda diz:
template <class T> void ref(const T&&) = delete; template <class T> void cref(const T&&) = delete;
fonte
const T&&
é usado?const T&&
evita que alguém use tolamente os argumentos de template explícitos do formulárioref<const A&>(...)
. Esse não é um argumento muito forte, mas o custo deconst T&&
terminarT&&
é mínimo.A semântica de obter uma referência const rvalue (e não para
=delete
) é para dizer:O seguinte caso de uso poderia ter sido IMHO um bom caso de uso para a referência rvalue para const , embora a linguagem tenha decidido não adotar essa abordagem (veja o post original do SO ).
O caso: construtor de smart pointers do ponteiro bruto
Normalmente seria aconselhável usar
make_unique
emake_shared
, mas ambosunique_ptr
eshared_ptr
podem ser construídos a partir de um ponteiro bruto. Ambos os construtores obtêm o ponteiro por valor e o copiam. Ambos permitem (ou seja, no sentido de: não evita ) um uso continuado do ponteiro original passado a eles no construtor.O código a seguir é compilado e resulta com 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
eshared_ptr
poderiam evitar o acima se seus construtores relevantes esperassem obter o ponteiro bruto como um valor const r , por exemplo, paraunique_ptr
:unique_ptr(T* const&& p) : ptr{p} {}
Nesse caso, o código duplo livre acima não seria compilado, mas o seguinte:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Observe que
ptr
ainda pode ser usado depois de movido, então o bug em potencial não desapareceu totalmente. Mas se o usuário for obrigado a chamarstd::move
tal bug, cairia na regra comum de: não use um recurso que foi movido.Pode-se perguntar: OK, mas por que
T*
const&& p
?A razão é simples, para permitir a criação de um
unique_ptr
ponteiro const . Lembre-se de que a referência const rvalue é mais genérica do que apenas a referência rvalue , pois aceita ambosconst
enon-const
. Portanto, podemos permitir o seguinte:int* const ptr = new int(9); auto p = std::unique_ptr<int> { std::move(ptr) };
isso não funcionaria se esperássemos apenas uma referência de rvalue (erro de compilação: não é possível vincular const rvalue a rvalue ).
De qualquer forma, é tarde demais para propor tal coisa. Mas essa ideia apresenta um uso razoável de uma referência rvalue para const .
fonte
Eles são permitidos e até mesmo funções classificadas com base em
const
, mas como você não pode mover do objeto const referido porconst Foo&&
, eles não são úteis.fonte
const T&, T&, const T&&, T&&
Além de std :: ref , a biblioteca padrão também usa a referência const rvalue em std :: as_const para o mesmo propósito.
template <class T> void as_const(const T&&) = delete;
Ele também é usado como valor de retorno em std :: optional ao obter o valor empacotado:
constexpr const T&& operator*() const&&; constexpr const T&& value() const &&;
Bem como em 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;
Isso presumivelmente é para manter a categoria de valor, bem como a constância do wrapper ao acessar o valor embalado.
Isso faz diferença se as funções qualificadas const rvalue ref podem ser chamadas no objeto empacotado. Dito isso, não conheço nenhum uso para as funções qualificadas const rvalue ref.
fonte
Não consigo pensar em uma situação em que isso seja útil diretamente, mas pode ser usado indiretamente:
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); }
OT em g é T const, então f 's x é um T const &&.
É provável que isso resulte em um erro de compilação em f (quando ele tenta mover ou usar o objeto), mas f pode tomar um rvalue-ref de forma que não possa ser chamado em lvalues, sem modificar o rvalue (como no muito simples exemplo acima).
fonte