为什么 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>
的理智的争论放在一边,这在以下方面工作得很好:
- 叮当响 Mac
- Visual Studio 对于 Windows
- 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> > >, = 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::swap
和 std::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]);
我有这样的代码:
#include <vector>
#include <utility>
int main()
{
std::vector<bool> vb{true, false};
std::swap(vb[0], vb[1]);
}
关于 vector<bool>
的理智的争论放在一边,这在以下方面工作得很好:
- 叮当响 Mac
- Visual Studio 对于 Windows
- 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> > >, = 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::swap
和 std::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]);