Eu tenho um código como este:
#include <vector>
#include <utility>
int main()
{
std::vector<bool> vb{true, false};
std::swap(vb[0], vb[1]);
}
Argumentos sobre a sanidade de vector<bool>
lado, isso estava funcionando bem:
- Clang for Mac
- Visual Studio para Windows
- GCC para Linux
Tentei compilar com o Clang no Windows e recebi o seguinte erro (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>&&
Estou surpreso que os resultados sejam diferentes nas implementações.
Por que não funciona com o Clang no Windows?
operator[]
um lvalue? e podestd::swap
operar com rvalues e xvalues?/permissive-
(conformidade), que geralmente deve ser usada de qualquer maneira;)Respostas:
O padrão não exige que isso seja compilado em nenhuma cadeia de ferramentas!
Primeiro, lembre-se de que
vector<bool>
é esquisito e de assinatura que fornece um objeto temporário do tipo proxy chamadostd::vector<bool>::reference
, em vez de realbool&
.A mensagem de erro está informando que ele não pode vincular esse temporário a uma
const
referência sem valor natemplate <typename T> std::swap(T& lhs, T& rhs)
implementação genérica .Extensões!
No entanto, acontece que o libstdc ++ define uma sobrecarga para
std::swap(std::vector<bool>::reference, std::vector<bool>::reference)
, mas essa é uma extensão do padrão (ou, se estiver lá, não consigo encontrar nenhuma evidência para isso).A libc ++ também faz isso .
Suponho que a implementação stdlib do Visual Studio, que você ainda está usando, não o faça , mas para adicionar insulto à lesão, você pode vincular temporariamente a referências de valor no VS (a menos que esteja usando o modo de conformidade). A função padrão "genérica"
std::swap
funciona até você substituir o compilador VS pelo compilador Clang mais rigoroso.Como resultado, você depende de extensões em todas as três cadeias de ferramentas para as quais funcionou para você, e a combinação Clang no Windows é a única que realmente exibe conformidade estrita.
(Na minha opinião, essas três cadeias de ferramentas deveriam ter diagnosticado isso para que você não enviasse código não portátil o tempo todo.)
E agora?
Pode ser tentador adicionar sua própria especialização de
std::swap
estd::vector<bool>::reference
, mas você não pode fazer isso para tipos padrão; de fato, entraria em conflito com as sobrecargas que libstdc ++ e libc ++ optaram por adicionar como extensões.Portanto, para ser portátil e compatível, você deve alterar seu código .
Talvez um bom antiquado:
Ou use a função de membro estático especial que faz exatamente o que você queria :
Também soletrável da seguinte forma:
fonte
std::vector<bool>::reference
que nada seja realmente mal formado. Para mim, parece que usar algo assimchar * foo = "bar";
exigiria um diagnóstico, pois isso é mal formado.