¿Por qué std :: string no proporciona conversión implícita a char *?

std::string proporciona const char * c_str () const que:

Obtener el equivalente de cadena C

Genera una secuencia de caracteres terminada en nulo (c-string) con el mismo contenido que el objeto de cadena y la devuelve como un puntero a una matriz de caracteres.

Un carácter nulo de terminación se agrega automáticamente.

La matriz devuelta apunta a una ubicación interna con el espacio de almacenamiento requerido para esta secuencia de caracteres más su carácter nulo de terminación, pero los valores en esta matriz no se deben modificar en el progtwig y solo se otorgan para permanecer sin cambios hasta la próxima llamada a una función de miembro no constante del objeto de cadena.

¿Por qué no definen simplemente el operator const char*() const {return c_str();} ?

Del lenguaje de progtwigción C ++ 20.3.7 (énfasis mío):

La conversión a una cadena estilo C podría haber sido proporcionada por un operador const char * () en lugar de c_str (). Esto habría proporcionado la conveniencia de una conversión implícita a costa de sorpresas en los casos en que dicha conversión era inesperada .

Veo al menos dos problemas con la conversión implícita:

  • Incluso la conversión explícita que proporciona c ___ str () es lo suficientemente peligrosa como es. He visto muchos casos en los que el puntero se almacenaba para usarse después de que la vida útil del objeto de cadena original había finalizado (o el objeto se había modificado, lo que invalidaba el puntero). Con la llamada explícita a c_str () es de esperar que conozca estos problemas. Pero con la conversión implícita sería muy fácil causar un comportamiento indefinido como en:

     const char *filename = string("/tmp/") + name; ofstream tmpfile(filename); // UB 
  • La conversión también ocurriría en algunos casos donde no lo esperarías y la semántica es sorprendente por decir lo menos:

     string name; if (name) // always true ; name-2; // pointer arithmetic + UB 

    Estos podrían evitarse de alguna manera, pero ¿por qué meterse en este problema en primer lugar?

El libro de Josuttis dice lo siguiente:

Esto es por razones de seguridad para evitar conversiones de tipo involuntarias que producen un comportamiento extraño (tipo char * menudo tiene un comportamiento extraño) y ambigüedades (por ejemplo, en una expresión que combine una string y una cadena C sería posible convertir string en char * y viceversa).

Porque las conversiones implícitas casi nunca se comportan como esperabas. Pueden dar resultados sorprendentes en la resolución de sobrecarga, por lo que generalmente es mejor proporcionar una conversión explícita como lo hace std :: string.

Probablemente sea porque esta conversión tendría una semántica sorprendente y peculiar. Particularmente, el cuarto párrafo que cita.

Otra razón es que hay una conversión implícita const char* -> string , y esto sería solo lo contrario, lo que significaría un comportamiento extraño wrt resolución de sobrecarga (no debe hacer las dos conversiones implícitas A->B y B->A )

Además de los motivos proporcionados en la especificación (sorpresas inesperadas), si está mezclando llamadas de C API con std :: string, realmente necesita adquirir el hábito de usar el método :: c_ctr (). Si alguna vez llama a una función varargs (por ejemplo: printf, o equivalente) que requiere un const char *, y pasa una cadena std :: directamente (sin llamar al método de extracción), no obtendrá un error de comstackción (ningún tipo verificando las funciones varargs), pero obtendrás un error de tiempo de ejecución (el diseño de clase no es binario idéntico a un const char *).

Incidentalmente, CString (en MFC) toma el enfoque opuesto: tiene un molde implícito, y el diseño de clase es compatible con binario con const char * (o const w_char *, si se comstack para cadenas de caracteres anchos, es decir: “Unicode”).

Debido a que las cadenas estilo C son una fuente de errores y muchos problemas de seguridad, es mucho mejor hacer la conversión explícitamente.