c++ - const_cast y std:: move para eliminar la constancia de la no referencia
c++11 move-semantics (3)
Como el resultado de la llamada de función es, por definición, un valor R en sí mismo, no es necesario aplicar std::move
en él en la declaración de retorno: const_cast<bar&&>(foo<bar>())
debería ser suficiente. Eso hace que el código sea un poco más fácil de leer.
Aún así, no existe una garantía estándar de que esto siempre funcionará para todos los tipos de bar
. Aún más: en algunos casos, esto podría llevar a un comportamiento indefinido (Imagine una optimización muy intrusiva, que elimina por completo a foo
y hace que su resultado sea un objeto en el segmento de "datos estáticos" de la memoria, como si foo
fuera una constexpr
. A continuación, se llama al constructor en movimiento. , que probablemente modifique su argumento, podría llevar a una excepción de infracción de acceso).
Todo lo que puede hacer es cambiar a una biblioteca diferente (o, si es posible, pedirle al encargado de la biblioteca que arregle la API) o crear una prueba de unidad e incluirla en su proceso de compilación, siempre que pase la prueba, debería estar bien (recuerde usar la misma configuración de optimización que en "producción" build - const_cast
es una de esas cosas que depende en gran medida de la configuración de compilación).
Tengo una biblioteca externa que no puedo modificar. La biblioteca declara una función de plantilla que, por algún motivo, devuelve un objeto no de referencia const
:
template<class C>
const C foo();
Tengo otra biblioteca externa que tampoco puedo modificar. La biblioteca declara una clase que no se puede copiar y tiene un constructor de movimiento solo desde un objeto no constante:
struct bar {
bar();
bar(const bar&)=delete;
bar(bar&&);
};
Ahora necesito usar foo<bar>
. Un uso simple:
bar buz() {
return foo<bar>();
}
falla con
main.cpp: In function ''bar buz()'': main.cpp:13:21: error: use of deleted function ''bar::bar(const bar&)'' return foo<bar>(); ^ main.cpp:8:5: note: declared here bar(const bar&)=delete; ^~~
lo que tiene sentido, y ninguna solución simple hace que el código se compile.
Sin embargo, si agrego una solución más compleja:
bar buz() {
return const_cast<bar&&>(std::move(foo<bar>()));
}
se compila y todo el código funciona como se esperaba (no solo el ejemplo simplificado anterior, sino también mi código real).
Sin embargo, ¿es seguro o me estoy topando con algún comportamiento indefinido? ¿Hay alguna solución mejor?
He leído y entiendo las preguntas sobre cómo devolver const
de las funciones ( 1 , 2 ), y la respuesta común parece ser que se desaconseja devolver objetos const
en C ++ moderno, pero mi pregunta no es sobre esto, sino sobre cómo puedo solucionarlo. La situación cuando una biblioteca externa devuelve const
objeto.
Deshacerse de la constante llevará a un comportamiento indefinido si el constructor de movimiento para la bar
modifica algo. Probablemente pueda solucionar su problema de esta manera sin introducir un comportamiento indefinido:
struct wrapped_bar {
mutable bar wrapped;
};
bar buz()
{
return foo<wrapped_bar>().wrapped;
}
El hecho de que el miembro wrapped
sea mutable significa que el miembro no es constante, aunque el objeto wrapped_bar
en su conjunto es const. Según cómo funcione foo()
, es posible que deba agregar miembros a wrapped_bar
para que funcione más como una bar
.
Técnicamente hablando, estás exponiendo tu programa a un comportamiento indefinido. Dado que el objeto original C
(un temporal) se declaró const
, la conversión y modificación constantes es ilegal y está en contra de la norma. (Supongo que mover constructor hace algunas modificaciones al mover).
Dicho esto, probablemente funcione en su entorno y no veo una solución mejor.