what str que mid library hace does c_str c++ string c++17 c-str

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 los data 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 reemplazar c_str por data , 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 de std::string de una función miembro no const .data() un descuido o un diseño intencional basado en la semántica std::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ámetro lpCommandLine de la función CreateProcess en la API de Windows. Debido a que el miembro data() de std::string es constante, no se puede usar para hacer que los objetos std :: string funcionen con el parámetro lpCommandLine . 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ón programName.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() , como std::vector con std::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.