¿Por qué la referencia de valor de C ++ 0x no es la predeterminada?
Pregunta
Una de las nuevas características interesantes del próximo estándar C ++, C ++ 0x, son & "; referencias de valor. &"; Una referencia rvalue es similar a una referencia lvalue (normal), excepto que puede vincularse a un valor temporal (normalmente, un temporal solo puede vincularse a una referencia const
):
void FunctionWithLValueRef(int& a) {...}
void FunctionWithRValueRef(int&& a) {...}
int main() {
FunctionWithLValueRef(5); // error, 5 is a temporary
FunctionWithRValueRef(5); // okay
}
Entonces, ¿por qué inventaron un tipo completamente nuevo, en lugar de simplemente eliminar las restricciones a las referencias normales para permitir que se unan a los temporales?
Solución
Sería inútil. Cambiaría la cosa en la función, y el cambio se perdería inmediatamente porque la cosa era en realidad temporal.
La razón del nuevo tipo se debe a la necesidad de poder decidir qué es realmente un valor y qué no. Solo entonces puedes usarlos para las cosas geniales que se usan.
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));
}
Ahora, si tiene un valor de r y lo pasa a toupper, el valor de r puede modificarse directamente, porque sabemos que el temporal es algo desechable de todos modos, por lo que también podemos cambiarlo y no es necesario copiar eso. Además, se usa la misma observación para la cosa llamada constructores de movimiento y asignación de movimiento. El lado derecho no se copia, pero sus cosas simplemente se roban y se mueven a *this
.
Si dijera que los valores pueden unirse a referencias de valor no constantes, entonces no tendría forma de averiguar si eso hace referencia a un valor (objeto nombrado) o un valor (temporal) al final.
Probablemente sea más poco conocido, pero útil de todos modos, puede poner lvalue o rvalue ref-qualifiers en una función miembro. Aquí hay un ejemplo, que naturalmente extiende la semántica existente de las referencias rvalue al parámetro de objeto implícito:
struct string {
string& operator=(string const& other) & { /* ... */ }
};
Ahora, ya no puedes decir
string() = "hello";
Lo cual es confuso y realmente no tiene sentido la mayor parte del tiempo. Lo que hace el &
anterior es decir que el operador de asignación solo se puede invocar en valores. Lo mismo se puede hacer para los valores, poniendo &&
.
Otros consejos
Porque agregar un nuevo tipo de referencia le permite escribir dos sobrecargas de un método:
void CopyFrom(MyClass &&c)
{
dataMember.swap(c);
}
void CopyFrom(const MyClass &c)
{
dataMember.copyTheHardWay(c);
}
La versión que acepta el nuevo tipo de referencia puede modificar la variable que recibe, porque esa variable no se utilizará en ningún otro lado. Entonces puede & "; Robar &"; el contenido de la misma.
Esta es la razón por la que se agregó esta característica; retener un tipo de referencia no lograría el objetivo deseado.