为什么std :: swap在Clang / Win下的vector <bool>元素上不起作用?

Lig*_*ica 14 c++ std clang visual-studio c++17

我有这样的代码:

#include <vector>
#include <utility>

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

关于是否理智的争论,在vector<bool>以下方面效果很好:

  • Mac版Clang
  • Windows的Visual Studio
  • 适用于Linux的GCC

然后,我尝试在Windows上使用Clang构建它,并收到以下错误(摘要):

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>&&
Run Code Online (Sandbox Code Playgroud)

对于不同的实现结果,我感到惊讶。

为什么Windows上的Clang不起作用?

Lig*_*ica 15

该标准不需要在任何工具链上进行编译!

首先回忆起来vector<bool>很奇怪,然后下标它给你一个临时的代理类型的对象std::vector<bool>::reference,而不是一个实际的对象bool&

错误消息告诉您,它不能将此临时绑定绑定到const通用template <typename T> std::swap(T& lhs, T& rhs)实现中的非左值引用。

扩展!

然而,事实证明,的libstdc ++ 定义过载std::swap(std::vector<bool>::reference, std::vector<bool>::reference),但是这是一个可扩展标准(或者,如果是在那里,我找不到它的任何证据)。

libc ++ 也这样做

我猜想您还在使用的Visual Studio stdlib实现并没有实现,但是为了增加伤害,您可以将临时对象绑定到 VS中的左值引用(除非您使用一致性模式),因此std::swap在您将VS编译器替换为更严格的Clang编译器之前,标准的“泛型” 功能才起作用。

结果,您一直依赖于它为您服务的所有三个工具链的扩展,并且Windows上的Clang组合是唯一真正表现出严格合规性的组合。

(我认为,这三个工具链应该已经对此进行了诊断,因此您不会一直都在运送不可移植的代码。)

现在怎么办?

添加您自己的std::swap和专长可能很吸引人std::vector<bool>::reference,但是您不允许在标准类型中使用;实际上,这将与libstdc ++和libc ++选择添加为扩展的重载冲突。

因此,为了便于移植和合规,您应该更改代码

也许是一个很好的老式:

const bool temp = vb[0];
vb[0] = vb[1];
vb[1] = temp;
Run Code Online (Sandbox Code Playgroud)

或者使用特殊的静态成员函数来实现您想要的功能

std::vector<bool>::swap(vb[0], vb[1]);
Run Code Online (Sandbox Code Playgroud)

也可拼写如下:

vb.swap(vb[0], vb[1]);
Run Code Online (Sandbox Code Playgroud)