Un compañero de trabajo quería escribir esto:
std::string_view strip_whitespace(std::string_view sv);
std::string line = "hello ";
line = strip_whitespace(line);
Dije que regresar string_view
me inquietaba a priori y, además, el alias aquí me parecía UB.
Puedo decir con certeza que line = strip_whitespace(line)
en este caso es equivalente a line = std::string_view(line.data(), 5)
. Creo que llamará string::operator=(const T&) [with T=string_view]
, que se define como equivalente a line.assign(const T&) [with T=string_view]
, que se define como equivalente a line.assign(line.data(), 5)
, que se define para hacer esto:
Preconditions: [s, s + n) is a valid range.
Effects: Replaces the string controlled by *this with a copy of the range [s, s + n).
Returns: *this.
Pero esto no dice qué sucede cuando hay alias.
Ayer hice esta pregunta en el cpplang Slack y obtuve respuestas mixtas. Buscando respuestas súper autorizadas aquí, y / o análisis empírico de implementaciones de vendedores de bibliotecas reales.
Escribí casos de prueba para string::assign
, vector::assign
, deque::assign
, list::assign
, y forward_list::assign
.
- Libc ++ hace que todos estos casos de prueba funcionen.
- Libstdc ++ hace que todos funcionen, excepto que
forward_list
, por defecto. - No sé sobre la biblioteca de MSVC.
La segfault en libstdc ++ me da la esperanza de que esto sea UB; pero también veo que tanto libc ++ como libstdc ++ están haciendo un gran esfuerzo para que esto funcione al menos en los casos comunes.
fuente
*this
. Pero no veo nada para evitar que el almacenamiento existente se reutilice, en cuyo caso esto no se especifica, ya que la semántica de copiar el almacenamiento no está especificada.std::string::assign
caso y para el caso del contenedor (std::vector::assign
específicamente) .assign
requisitos en [tab: container.seq.req] .Respuestas:
Salvo un par de excepciones de las cuales la suya no es una, llamar a una función miembro no constante (es decir
assign
) en una cadena invalida [...] los punteros a sus elementos. Esto viola la condición previa deassign
que[s, s + n)
es un rango válido, por lo que este es un comportamiento indefinido.Tenga en cuenta que
string::operator=(string const&)
tiene un lenguaje específico para hacer que la autoasignación no sea una operación.fuente
assign
? En caso afirmativo, tendríamos que establecer un punto específico dentro de la implementación de asignar para marcar cuándo puede ocurrir exactamente la invalidación, y creo que eso no es algo que C ++ haría. Aunque podría estar equivocado.std::vector::insert(iterator pos, const T& value)
debe funcionar sivalue
está dentro del vector, porque el estándar no especifica que está permitido no funcionar, aunque la referencia pueda invalidar esa referencia.