为什么 std::swap 不适用于<bool> Clang/Win(英语:Clang/Win)下的矢量元素?

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

本文关键字:Win Clang 元素 英语 bool 不适用 lt gt 为什么 std swap      更新时间:2023-10-16

我有这样的代码:

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

除了关于vector<bool>的健全性的争论之外,这在上运行得很好

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

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

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

为什么它不适用于Windows上的Clang?

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

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

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

扩展

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

libc++也能做到这一点。

我猜您仍在使用的Visual Studio stdlib实现没有,但雪上加霜的是,您可以将临时库绑定到VS中的左值引用(除非您使用一致性模式(,因此标准的"泛型"std::swap函数可以工作,直到您用VS编译器替换更严格的Clang编译器。

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

(在我看来,这三个工具链应该已经诊断出了这一点,这样你就不会一直发布不可移植的代码。 )

现在怎么办

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

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

也许是一个不错的老式:

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

或者使用特殊的静态成员函数,它可以做你想要的事情:

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

也可拼写如下:

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