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?
operator[]
un valor? y puedestd::swap
operar con valores y valores x?/permissive-
(conformidad), que generalmente debe usarse de todos modos;)Respuestas:
¡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 llamadostd::vector<bool>::reference
, en lugar de un realbool&
.El mensaje de error le indica que no puede vincular este temporal a una
const
referencia que no sea de valor en latemplate <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::swap
funciona 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::swap
ystd::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:
O utilice la función especial de miembro estático que hace exactamente lo que quería :
También se deletrea de la siguiente manera:
fuente
std::vector<bool>::reference
para 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.