Por que C ++ 0x rvalue referência não é o padrão?
Pergunta
Um dos fresco novos recursos da próxima C ++ padrão, C ++ 0x, são "referências rvalue." Uma referência rvalue é semelhante a uma referência Ivalue (normal), a não ser que possa ser ligado a um valor temporário (normalmente, uma lata temporário só pode ser ligado a uma referência const
):
void FunctionWithLValueRef(int& a) {...}
void FunctionWithRValueRef(int&& a) {...}
int main() {
FunctionWithLValueRef(5); // error, 5 is a temporary
FunctionWithRValueRef(5); // okay
}
Então, por que inventar um novo tipo, em vez de apenas remover as restrições sobre referências normais para permitir que eles sejam obrigados a temporários?
Solução
Seria inútil. Você mudaria a coisa na função, ea mudança seria perdido imediatamente, porque a coisa era realmente um temporário.
A razão para o novo tipo decorre da necessidade de ser capaz de decidir o que realmente é um rvalue e quais não. Só então você pode realmente usá-los para as coisas legais que eles são usados.
string toupper(string && s) { // for nonconst rvalues
for(char &c : s) make_uppercase(c);
return move(s); // move s into a returned string object
}
string toupper(string const& s) { // for the rest
// calls the rvalue reference version, by passing
// an rvalue copy.
return toupper(string(s));
}
Agora, se você tem algum rvalue e passá-lo para toupper, o rvalue pode ser diretamente modificado, porque sabemos que o temporário é uma coisa descartável de qualquer maneira, para que possamos aswell apenas alterá-lo e não precisa copiar isto. Além disso, a mesma observação é usado para a coisa chamada Move-construtores e move-atribuição. O lado direito não é copiado, mas suas coisas estão apenas roubado e se mudou para *this
.
Se você dissesse que rvalues ??pode ligar-se a referências lvalue não-const, então você não teria nenhuma maneira de descobrir se que referencia um lvalue (nomeado objeto) ou um rvalue (temporária) no final.
É provavelmente pouco saber mais, mas de qualquer maneira útil, você pode colocar ref-eliminatórias lvalue ou rvalue em uma função de membro. Aqui está um exemplo, o que naturalmente se estende a semântica existentes de referências rvalue para o parâmetro de objeto implícito:
struct string {
string& operator=(string const& other) & { /* ... */ }
};
Agora, você não pode mais dizer
string() = "hello";
O que é confuso e não é realmente fazer sentido na maioria das vezes. O que o &
acima faz é dizer que o operador de atribuição só pode ser invocado em lvalues. O mesmo pode ser feito para rvalues, colocando &&
.
Outras dicas
Como adicionar um novo tipo de referência permite que você escreva duas sobrecargas de um método:
void CopyFrom(MyClass &&c)
{
dataMember.swap(c);
}
void CopyFrom(const MyClass &c)
{
dataMember.copyTheHardWay(c);
}
A versão que aceita o novo tipo de referência é permitido modificar a variável que recebe, porque essa variável não vai ser usado em qualquer outro lugar. Assim, ele pode "roubar" o conteúdo do mesmo.
Esta é toda a razão foi adicionado esse recurso; mantendo um tipo de referência não alcançar o objetivo desejado.