¿Por qué std :: swap no funciona en elementos vectoriales <bool> en Clang / Win?

14

Tengo un código como este:

#include <vector>
#include <utility>

int main()
{
   std::vector<bool> vb{true, false};
   std::swap(vb[0], vb[1]);
}

Argumentos sobre la cordura de vector<bool>un lado, esto estaba funcionando bien en:

  • Clang para Mac
  • Visual Studio para Windows
  • GCC para Linux

Luego intenté compilarlo con Clang en Windows y recibí el siguiente error (abreviado):

error: no matching function for call to 'swap'
                                std::swap(vb[0], vb[1]);
                                ^~~~~~~~~

note: candidate function [with _Ty = std::_Vb_reference<std::_Wrap_alloc<std::allocator<unsigned int> > >, $1 = void] not viable: expects an l-value for 1st argument
inline void swap(_Ty& _Left, _Ty& _Right) _NOEXCEPT_COND(is_nothrow_move_constructible_v<_Ty>&&

Me sorprende que los resultados difieran entre las implementaciones.

¿Por qué no funciona con Clang en Windows?

Carreras de ligereza en órbita
fuente
Entonces, supongo que la aclaración necesaria es: ¿Es el resultado de operator[]un valor? y puede std::swapoperar con valores y valores x?
Mgetz el
@Mgetz Sí. No. En ese orden. Esta pregunta se hizo "de verdad" en privado el otro día, y pensé que era suficientemente entretenida que la respuesta era "Clang / Win no está roto; el código estuvo roto todo este tiempo, pero los combos de cadenas de herramientas convencionales nunca se molestaron en decirte "para escribirlo aquí: P
Las carreras de ligereza en órbita el
2
Al igual que para su información, esto no se compila en VS 2019 con /permissive-(conformidad), que generalmente debe usarse de todos modos;)
ChrisMM
1
@ChrisMM De hecho! El modo de desactivación desactivado era parte del rompecabezas. (¡Aunque no lo sabíamos antes de investigarlo!) Y mi respuesta sí lo señala: P
Las carreras de ligereza en órbita el

Respuestas:

15

¡El estándar no requiere que esto se compile en ninguna cadena de herramientas!

Primero recuerde que vector<bool>es extraño y suscribirlo le da un objeto temporal de un tipo de proxy llamado std::vector<bool>::reference, en lugar de un real bool&.

El mensaje de error le indica que no puede vincular este temporal a una constreferencia que no sea de valor en la template <typename T> std::swap(T& lhs, T& rhs)implementación genérica .

Extensiones!

Sin embargo, resulta que libstdc ++ define una sobrecarga para std::swap(std::vector<bool>::reference, std::vector<bool>::reference), pero esta es una extensión del estándar (o, si está allí, no puedo encontrar ninguna evidencia de ello).

libc ++ también hace esto .

Supongo que la implementación de Visual Studio stdlib, que todavía está usando, no lo hace , pero para agregar un insulto a la lesión , puede vincular los temporales a las referencias de valor en VS (a menos que esté usando el modo de conformidad), por lo que La función estándar, "genérica", std::swapfunciona hasta que sustituya el compilador VS por el compilador Clang más estricto.

Como resultado, ha estado confiando en extensiones en las tres cadenas de herramientas para las que funcionó, y la combinación de Clang en Windows es la única que realmente muestra un cumplimiento estricto.

(En mi opinión, esas tres cadenas de herramientas deberían haber diagnosticado esto para que no haya enviado código no portátil todo este tiempo..)

¿Ahora que?

Puede ser tentador agregar su propia especialización de std::swapy std::vector<bool>::reference, pero no puede hacerlo para los tipos estándar; de hecho, entraría en conflicto con las sobrecargas que libstdc ++ y libc ++ han elegido agregar como extensiones.

Por lo tanto, para ser portátil y compatible, debe cambiar su código .

Quizás un buen anticuado:

const bool temp = vb[0];
vb[0] = vb[1];
vb[1] = temp;

O utilice la función especial de miembro estático que hace exactamente lo que quería :

std::vector<bool>::swap(vb[0], vb[1]);

También se deletrea de la siguiente manera:

vb.swap(vb[0], vb[1]);
Carreras de ligereza en órbita
fuente
En cuanto a, pero no se supone que AFAIK, se les permite hacerlo. Siempre que no rompan el código conforme, pueden extender la implementación para hacer que el código roto sea "OK".
NathanOliver el
@ NathanOliver-ReinstateMonica Bueno, está bien. Sin embargo, ¿no tienen que al menos diagnosticar el uso de tales cosas? eel.is/c++draft/intro.compliance#8
Carreras de ligereza en órbita el
@LightnessRaceswithMonica ¿hay algún lenguaje que prohíba esta extensión?
Mgetz el
@Mgetz Lo sentimos, no estoy versado en todos los idiomas existentes, así que no puedo responder a eso
Luminosidad razas en órbita
No estoy seguro si se aplica el uso de tales extensiones que están mal formadas de acuerdo con este documento . Agregaron una sobrecarga que requiere un sistema std::vector<bool>::referencepara que nada esté mal formado. Para mí, parece que usar algo así char * foo = "bar";requeriría un diagnóstico ya que está mal formado.
NathanOliver el