c++ language-lawyer c++17 dangling-pointer string-view

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 tipo T1 a un tipo de destino relacionado con el tipo T2 de la expresión de funcionamiento E2 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 y T2 son del mismo tipo de clase [...]
    • de lo contrario, si T2 es una clase base de T1 , [...]
    • 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! }

https://godbolt.org/z/1Hu_p2

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! }

https://godbolt.org/z/Q-sEkr

¿Por qué es este el caso? ¿Por qué el código original crea una copia implícita sin aviso?