为什么 std::swap 不能处理 Clang/Win 下的 vector<bool> 元素?

Why doesn't std::swap work on vector<bool> elements under Clang/Win?

我有这样的代码:

#include <vector>
#include <utility>

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

关于 vector<bool> 的理智的争论放在一边,这在以下方面工作得很好:

然后我尝试在 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> > >,  = 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>&&

我很惊讶不同实施的结果不同。

为什么它不能在 Windows 上使用 Clang?

标准不要求在任何工具链上编译!

首先回想一下 vector<bool> 很奇怪,下标会为您提供一个名为 std::vector<bool>::reference 的代理类型的临时对象,而不是实际的 bool&.

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

扩展!

然而,事实证明 libstdc++ defines an overload for std::swap(std::vector<bool>::reference, std::vector<bool>::reference),但这是对标准的扩展(或者,如果它在那里,我找不到任何证据) ).

libc++ does this too.

我猜你仍在使用的 Visual Studio stdlib 实现 不会 ,但雪上加霜 you can bind temporaries to lvalue references 在 VS 中(除非您使用一致性模式),因此标准 "generic"、std::swap 函数一直有效,直到您将 VS 编译器替换为更严格的 Clang 编译器。

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

(在我看来,这三个工具链 should have diagnosed this 所以你一直没有发布不可移植的代码。)

现在怎么办?

添加您自己的 std::swapstd::vector<bool>::reference 专业化可能很诱人,但不允许您对标准类型执行此操作;事实上,它会与 libstdc++ 和 libc++ 选择添加为扩展的重载冲突。

因此,为了便携和兼容,您应该更改代码

也许老套的好:

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

或使用the special static member function that does exactly what you wanted:

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

又拼写如下:

vb.swap(vb[0], vb[1]);