Estoy usando una biblioteca interna que fue diseñada para imitar una biblioteca C ++ propuesta , y en algún momento en los últimos años veo que su interfaz cambió de usar std::string
a string_view
.
Así que obedientemente cambio mi código, para adaptarme a la nueva interfaz. Desafortunadamente, lo que tengo que pasar es un parámetro std :: string, y algo que es un valor de retorno std :: string. Entonces mi código cambió de algo como esto:
void one_time_setup(const std::string & p1, int p2) {
api_class api;
api.setup (p1, special_number_to_string(p2));
}
a
void one_time_setup(const std::string & p1, int p2) {
api_class api;
const std::string p2_storage(special_number_to_string(p2));
api.setup (string_view(&p1[0], p1.size()), string_view(&p2_storage[0], p2_storage.size()));
}
Yo realmente no veo lo que este cambio me compró como el cliente de API, que no sea más código (posiblemente a meter la pata). La llamada a la API es menos segura (debido a que la API ya no posee el almacenamiento para sus parámetros), probablemente guardó el trabajo de mi programa 0 (debido a las optimizaciones de movimiento que los compiladores pueden hacer ahora), e incluso si ahorrara trabajo, eso solo sería un par de asignaciones que no se realizarán y nunca se realizarán después del inicio o en un gran bucle en alguna parte. No es para esta API.
Sin embargo, este enfoque parece seguir los consejos que veo en otros lugares, por ejemplo, esta respuesta :
Por otro lado, desde C ++ 17 debe evitar pasar un const std :: string & a favor de un std :: string_view:
Encuentro sorprendente este consejo, ya que parece estar abogando por el reemplazo universal de un objeto relativamente seguro por uno menos seguro (básicamente un puntero y una longitud glorificados), principalmente con fines de optimización.
Entonces, ¿cuándo se debe usar string_view y cuándo no?
fuente
std::string_view
constructor directamente, solo debe pasar las cadenas al método que toma unstd::string_view
directamente y se convertirá automáticamente.<string>
encabezado y sucede automáticamente. Ese código es engañoso e incorrecto.Respuestas:
std::string
(no const, no ref). Esta opción le da la opción de moverse explícitamente en un valor también si sabe que nunca más se usará en el contexto de la llamada.std::string_view
(const, no ref) esto se debe a questring_view
puede manejarstd::string
ychar*
fácilmente sin problemas y sin hacer una copia. Esto debería reemplazar todos losconst std::string&
parámetros.Finalmente, nunca deberías necesitar llamar al
std::string_view
constructor como eres.std::string
tiene un operador de conversión que maneja la conversión automáticamente.fuente
std::string_view
sea más fácil de usar. Si un desarrollador lo usa mal en una situación de propiedad, es un error de programación.std::string_view
Es estrictamente no propietario.const, non-ref
? El parámetro const depende del uso específico, pero en general es razonable como no const. Y te perdiste 3. Puede aceptar rebanadasconst std::string_view &
en lugar deconst std::string &
?A
std::string_view
trae algunos de los beneficios de aconst char*
a C ++: a diferencia destd::string
, string_viewstd::string&
.Esto significa que string_view a menudo puede evitar copias, sin tener que lidiar con punteros sin formato.
En el código moderno,
std::string_view
debería reemplazar casi todos los usos de losconst std::string&
parámetros de función. Esto debería ser un cambio compatible con la fuente, ya questd::string
declara un operador de conversión astd::string_view
.El hecho de que una vista de cadena no ayude en su caso de uso específico en el que necesita crear una cadena de todos modos no significa que sea una mala idea en general. La biblioteca estándar de C ++ tiende a optimizarse para generalidad más que para conveniencia. El argumento "menos seguro" no se cumple, ya que no debería ser necesario crear la vista de cadena usted mismo.
fuente
std::string_view
es la ausencia de unc_str()
método, lo que resulta enstd::string
objetos intermedios innecesarios que deben construirse y asignarse. Esto es especialmente un problema en las API de bajo nivel.abcdef\0
y una vista de cadena que apunta a lacde
subcadena, no hay ningún carácter cero después de lae
cadena original tiene unf
allí. El estándar también señala: “data () puede devolver un puntero a un búfer que no tiene terminación nula. Por lo tanto, generalmente es un error pasar data () a una función que solo toma un const charT * y espera una cadena con terminación nula. ”Creo que esto es un poco malentendido el propósito de esto. Si bien es una "optimización", realmente deberías pensar en ello como liberarte de tener que usar a
std::string
.Los usuarios de C ++ han creado docenas de diferentes clases de cadenas. Clases de cadenas de longitud fija, clases optimizadas para SSO con el tamaño del búfer como un parámetro de plantilla, clases de cadenas que almacenan un valor hash utilizado para compararlas, etc. Algunas personas incluso usan cadenas basadas en COW. Si hay algo que a los programadores de C ++ les encanta hacer, es escribir clases de cadena.
Y eso ignora las cadenas que son creadas y propiedad de las bibliotecas C. Desnudo
char*
, tal vez con un tamaño de algún tipo.Entonces, si está escribiendo alguna biblioteca y toma una
const std::string&
, el usuario ahora tiene que tomar la cadena que estaba usando y copiarla en astd::string
. Tal vez docenas de veces.Si desea acceder a
std::string
la interfaz específica de la cadena, ¿por qué debería copiar la cadena? Eso es un desperdicio.Las razones principales para no tomar a
string_view
como parámetro son:Si su objetivo final es pasar la cadena a una interfaz que toma una cadena terminada en NUL (
fopen
, etc.).std::string
está garantizado para ser NUL terminado;string_view
no lo es Y es muy fácil substraer una vista para que no tenga terminación NUL; sub-encadenar unstd::string
copiará la subcadena a cabo en un rango NUL terminados.Escribí un tipo especial de estilo string_view terminado en NUL para exactamente este escenario. Puede realizar la mayoría de las operaciones, pero no las que rompen su estado terminado por NUL (recorte desde el final, por ejemplo).
Problemas de por vida. Si realmente necesita copiar eso
std::string
o si la matriz de caracteres sobrevive a la llamada a la función, es mejor declarar esto por adelantado tomando unconst std::string &
. O simplementestd::string
como un parámetro de valor. De esa manera, si ya tienen una cadena de este tipo, puede reclamar su propiedad de inmediato, y la persona que llama puede moverse a la cadena si no necesita guardar una copia.fuente
string_view
es un tipo de lengua franca que puede funcionar con cualquier cosa.string_view
, es fácil.