c++ - ¿Por qué std:: string_view crea una vista colgada en una expresión ternaria?
language-lawyer c++17 (1)
Porque así es como funciona el operador condicional.
¿Está invocando
?:
En dos operandos, uno de los cuales es un lvalue de tipo
std::string const
y el otro es un lvalue de tipo
char const[1]
.
La regla de lenguaje para el operador condicional es ... realmente complicada.
La
regla relevante
es:
De lo contrario, si el segundo y tercer operando tienen tipos diferentes y tienen un tipo de clase (posiblemente cv calificado) , o si ambos son valores de la misma categoría de valor y el mismo tipo, excepto para la calificación de cv , se intenta formar un secuencia de conversión implícita de cada uno de esos operandos al tipo del otro. [ Nota : las propiedades como el acceso, si un operando es un campo de bits o si se elimina una función de conversión se ignoran para esa determinación. - nota final] Se realizan intentos para formar una secuencia de conversión implícita de una expresión de operador
E1
de tipoT1
a un tipo de destino relacionado con el tipoT2
de la expresión de funcionamientoE2
siguiente manera:
- Si E2 es un lvalue, el tipo de destino es "lvalue reference to
T2
", sujeto a la restricción de que en la conversión la referencia debe vincularse directamente ([dcl.init.ref]) a un glvalue.- Si E2 es un xvalor, [...]
Si E2 es un prvalue o si no se puede formar ninguna de las secuencias de conversión anteriores y al menos uno de los operandos tiene un tipo de clase (posiblemente cv-calificado) :
- si
T1
yT2
son del mismo tipo de clase [...]- de lo contrario, si
T2
es una clase base deT1
, [...]- de lo contrario, el tipo de destino es el tipo que tendría E2 después de aplicar las conversiones estándar de lvalue-to-rvalue, array-to-pointer y function-to-pointer.
Usando este proceso, se determina si se puede formar una secuencia de conversión implícita desde el segundo operando hasta el tipo de destino determinado para el tercer operando, y viceversa. Si se pueden formar ambas secuencias, o se puede formar una, pero es la secuencia de conversión ambigua, el programa está mal formado. Si no se puede formar una secuencia de conversión, los operandos se dejan sin cambios y se realiza una verificación adicional como se describe a continuación. De lo contrario, si se puede formar exactamente una secuencia de conversión, esa conversión se aplica al operando elegido y el operando convertido se usa en lugar del operando original por el resto de esta subcláusula. [ Nota : la conversión podría estar mal formada incluso si se pudiera formar una secuencia de conversión implícita. - nota final ]
No se puede convertir
std::string const
a
char const(&)[1]
o
char const*
, pero se
puede convertir
char const[1]
a
std::string const
(la viñeta anidada interna) ... así que eso es lo que obtienes.
Un prvalue de tipo
std::string const
.
Es decir, está copiando una cadena o construyendo una nueva ... de cualquier manera, está devolviendo una
string_view
a un temporal que queda fuera del alcance de inmediato.
Lo que quieres es lo que tenías:
std::string_view myMethod(bool bla) {
return bla ? std::string_view(otherMethod()) : "";
}
o:
std::string_view myMethod(bool bla) {
return bla ? otherMethod() : ""sv;
}
El resultado de ese operador condicional es una
string_view
, con ambas conversiones seguras.
Considere un método que devuelva un
std::string_view
desde un método que devuelva un
const std::string&
o desde una cadena vacía.
Para mi sorpresa, escribir el método de esta manera da como resultado una vista de cadena colgando:
const std::string& otherMethod();
std::string_view myMethod(bool bla) {
return bla ? otherMethod() : ""; // Dangling view!
}
Parece que el compilador primero coloca una copia temporal
std::string
del resultado de otro
otherMethod()
en la pila y luego devuelve una vista de esta copia temporal en lugar de simplemente devolver una vista de la referencia.
Primero pensé en un error de comipler, pero tanto G ++ como clang hacen esto.
La solución es fácil: envolver otro
otherMethod
en una construcción explícita de
string_view
resuelve el problema:
std::string_view myMethod(bool bla) {
return bla ? std::string_view(otherMethod()) : ""; // Works as intended!
}
¿Por qué es este el caso? ¿Por qué el código original crea una copia implícita sin aviso?