c++ language-lawyer c++-standard-library

c++ - ¿Es válido pasar nullptr a std:: string:: assign?



language-lawyer c++-standard-library (4)

Tengo una función que devuelve un puntero y una longitud, y quiero llamar a std::string::assign(pointer, length) . ¿Tengo que hacer un caso especial ( clear ) cuando la longitud es cero y el puntero puede ser nullptr?

El estándar de C ++ dice:

21.4.6.3 basic_string::assign basic_string& assign(const charT* s, size_type n); Requires: s points to an array of at least n elements of charT.

Entonces, ¿y si n es cero? ¿Qué es una matriz de cero caracteres y cómo se señala uno? Es valido llamar

s.assign(nullptr, 0);

¿O es un comportamiento indefinido?

La implementación de libstdc ++ parece no desreferir los punteros s cuando el tamaño n es cero, pero eso no es una garantía.


Bueno, como señala, el estándar dice " s apunta a una matriz ...". Un puntero nulo no apunta a una matriz de ningún número de elementos. Ni siquiera 0 elementos. Además, tenga en cuenta que s apunta a "una matriz de al menos n elementos ...". Así que está claro que si n es cero, aún puede pasar un puntero legítimo a una matriz.

En general, la API de std::string no está bien protegida contra los punteros nulos a charT . Por lo tanto, siempre debe asegurarse de que los punteros que le entregue no sean nulos.


De la norma (2.14.7) [lex.nullptr]:

El puntero literal es la palabra clave nullptr. Es un prvalue de tipo std::nullptr_t . [ Nota: std::nullptr_t es un tipo distinto que no es un tipo de puntero ni un puntero al tipo de miembro ...]

std::nullptr_t puede convertirse implícitamente a cualquier tipo de puntero nulo según 4.10.1 [conv.ptr]. Independientemente del tipo de puntero nulo, el hecho es que no apunta a nada .

Por lo tanto, no cumple con el requisito de que s apunta a una matriz de al menos n elementos de charT .

Parece ser un comportamiento indefinido.

Curiosamente, de acuerdo con esta respuesta , el estándar C ++ 11 estableció claramente que s no debe ser un puntero nulo en el constructor de basic_string , pero esta redacción se ha eliminado desde entonces.


No estoy seguro de por qué una implementación eliminaría cualquier puntero a una matriz cuya longitud se proporcione como cero.

Dicho esto, me equivocaría al lado de la precaución. Podría argumentar que no está cumpliendo con el requisito de las normas:

21.4.6.3 basic_string :: asignar

8 Requiere: s apunta a una matriz de al menos n elementos de charT

porque nullptr no está apuntando a una matriz .

Así que técnicamente el comportamiento es indefinido.


Pedante, un nullptr no cumple los requisitos de apuntar a una matriz de tamaño >=0 , y por lo tanto el estándar no garantiza el comportamiento (es UB).

Por otro lado, a la implementación no se le permitiría desreferenciar el puntero si n es cero, porque el puntero podría estar en una matriz de tamaño cero, y la desreferenciación de dicho puntero tendría un comportamiento indefinido. Además, no habría necesidad de hacerlo, porque no se copia nada.

El razonamiento anterior no significa que esté bien ignorar la UB. Pero, si no hay ninguna razón para no permitir el s.assign(nullptr, 0) entonces sería preferible cambiar la redacción de la norma a "Si n es mayor que cero, entonces s apunta a ...". No conozco ninguna buena razón para rechazarlo, pero tampoco puedo prometer que no existe una buena razón.

Tenga en cuenta que agregar un cheque no es complicado:

s.assign(ptr ? ptr : "", n);

¿Qué es una matriz de cero caracteres?

Esto es: new char[0] . Las matrices de almacenamiento automático o estático pueden no tener un tamaño cero.