c++ - que - c_str() vs. data() cuando se trata del tipo de retorno
string library c++ (4)
La nueva sobrecarga fue agregada por P0272R1 para C ++ 17. Ni el documento en sí ni los enlaces en él discuten por qué solo a los data
se les asignaron nuevas sobrecargas, pero c_str
no. Solo podemos especular en este punto (a menos que las personas involucradas en la discusión estén de acuerdo), pero me gustaría ofrecer los siguientes puntos para su consideración:
Incluso solo agregar la sobrecarga a los
data
rompió algún código; Mantener este cambio conservador era una forma de minimizar el impacto negativo.La función
c_str
ha sido hasta ahora completamente idéntica a losdata
y es efectivamente una facilidad "heredada" para el código de interconexión que toma "cadena C", es decir, una matriz de caracteres inmutable y terminada en nulo. Ya que siempre puede reemplazarc_str
pordata
, no hay ninguna razón particular para agregar a esta interfaz heredada.
Me doy cuenta de que la motivación misma para P0292R1 era que existen API heredadas que, erróneamente o por razones C, solo tienen punteros mutables aunque no muten. De todos modos, supongo que no queremos agregar más a la API ya masiva de la cadena que es absolutamente necesario.
Un punto más: a partir de C ++ 17 ahora puede escribir en el terminador nulo, siempre que escriba el valor cero. (Anteriormente, solía ser UB para escribir algo en el terminador nulo). Un c_str
mutable crearía otro punto de entrada en esta sutileza particular, y c_str
menos sutilezas tengamos, mejor.
Después de C ++ 11, pensé en c_str()
y data()
equivalently .
C ++ 17 introduce una sobrecarga para este último, que devuelve un puntero no constante ( reference , que no estoy seguro de si se ha actualizado completamente con C ++ 17):
const CharT* data() const; (1)
CharT* data(); (2) (since C++17)
c_str()
solo devuelve un puntero constante:
const CharT* c_str() const;
¿Por qué la diferenciación de estos dos métodos en C ++ 17, especialmente cuando C ++ 11 fue el que los hizo homogéneos? En otras palabras, ¿por qué solo uno de los métodos tuvo una sobrecarga, mientras que el otro no?
La razón por la cual el miembro de data()
obtuvo una sobrecarga se explica en this documento en open-std.org.
TL; DR del documento : la función miembro no const .data()
para std::string
se agregó para mejorar la uniformidad en la biblioteca estándar y para ayudar a los desarrolladores de C ++ a escribir el código correcto. También es conveniente cuando se llama a una función de biblioteca C que no tiene una calificación constante en sus parámetros de cadena C.
Algunos pasajes relevantes del papel:
Resumen
¿Es la falta destd::string
de una función miembro no const.data()
un descuido o un diseño intencional basado en la semánticastd::string
pre-C ++ 11? En cualquier caso, esta falta de funcionalidad tienta a los desarrolladores a utilizar alternativas inseguras en varios escenarios legítimos. Este documento argumenta la adición de una función miembro no const.data()
para std :: string para mejorar la uniformidad en la biblioteca estándar y para ayudar a los desarrolladores de C ++ a escribir el código correcto.Casos de uso
Las bibliotecas de C en ocasiones incluyen rutinas que tienen parámetros char *. Un ejemplo es el parámetrolpCommandLine
de la funciónCreateProcess
en la API de Windows. Debido a que el miembrodata()
destd::string
es constante, no se puede usar para hacer que los objetos std :: string funcionen con el parámetrolpCommandLine
. Los desarrolladores están tentados a usar.front()
lugar, como en el siguiente ejemplo.
std::string programName; // ... if( CreateProcess( NULL, &programName.front(), /* etc. */ ) ) { // etc. } else { // handle error }
Tenga en cuenta que cuando
programName
está vacío, la expresiónprogramName.front()
provoca un comportamiento indefinido. Una cadena C temporal vacía corrige el error.
std::string programName; // ... if( !programName.empty() ) { char emptyString[] = {''/0''}; if( CreateProcess( NULL, programName.empty() ? emptyString : &programName.front(), /* etc. */ ) ) { // etc. } else { // handle error } }
Si hubiera un miembro no const
.data()
, comostd::vector
constd::vector
, el código correcto sería sencillo.
std::string programName; // ... if( !programName.empty() ) { char emptyString[] = {''/0''}; if( CreateProcess( NULL, programName.data(), /* etc. */ ) ) { // etc. } else { // handle error } }
Una función miembro no-const
.data() std::string
también es conveniente cuando se llama a una función de biblioteca C que no tiene calificación const en sus parámetros de cadena C. Esto es común en los códigos más antiguos y en aquellos que necesitan ser portátiles con compiladores C más antiguos.
Las dos funciones miembro c_str y los datos de std :: string existen debido al historial de la clase std :: string.
Hasta C ++ 11, una cadena std :: podría haberse implementado como copia en escritura. La representación interna no necesitaba ninguna terminación nula de la cadena almacenada. La función miembro c_str se aseguró de que la cadena devuelta terminara en nulo. El simlpy de datos de función miembro devolvió un puntero a la cadena almacenada, que no necesariamente terminó en nulo. - Para asegurarse de que se notaron cambios en la cadena para habilitar la copia en escritura, ambas funciones deben devolver un puntero a datos const.
Todo esto cambió con C ++ 11 cuando la copia en escritura ya no estaba permitida para std :: string. Dado que aún se requiere c_str para entregar una cadena terminada en nulo, la nula siempre se agrega a la cadena almacenada real. De lo contrario, es posible que una llamada a c_str deba cambiar los datos almacenados para que la cadena finalice en nulo, lo que haría que c_str no sea una función constante. Dado que los datos entregan un puntero a la cadena almacenada, generalmente tiene la misma implementación que c_str . Ambas funciones todavía existen debido a la compatibilidad con versiones anteriores.
Solo depende de la semántica de "qué quieres hacer con eso". En general, std::string
se usa a veces como un vector de búfer, es decir, como un reemplazo de std::vector<char>
. Esto se puede ver en boost::asio
menudo. En otras palabras, es una matriz de personajes.
c_str()
: significa estrictamente que estás buscando una cadena terminada en nulo. En ese sentido, nunca debe modificar los datos y nunca debe necesitar la cadena como no const.
data()
: es posible que necesite la información dentro de la cadena como datos de búfer, e incluso como no const. Puede o no necesitar modificar los datos, lo que puede hacer, siempre que no implique cambiar la longitud de la cadena.