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


14

我有这样的代码:

#include <vector>
#include <utility>

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

关于是否理智的争论,在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>&&

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

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


所以我想需要澄清的是:是operator[]左值的结果吗?并可以std::swap在rvalues和xvalues上运行?
Mgetz,

@Mgetz是的。不,按此顺序。这个问题被问“真正的”私人的一天,而且我认为这充分娱乐,答案是“铛/赢不破;代码被打破这整个的时间,但主流工具链连击从来不屑地告诉你”:在这里写下来:P
轻轨赛于

2
就像FYI一样,它不会在VS 2019中与/permissive-(一致性)一起编译,无论如何通常应该使用它;)
ChrisMM

1
@ChrisMM确实!关闭一致性模式是难题的一部分。(尽管我们在研究之前不知道这一点!)而且我的回答确实指出:P
轻轨赛于

Answers:


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;

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

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

也可拼写如下:

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

关于,但是我们不应该允许这样做。只要他们不破坏一致的代码,就可以扩展实现以使破坏的代码“确定”。
NathanOliver

@ NathanOliver-ReinstateMonica好吧。但是,他们是否不必至少诊断出使用这些东西?eel.is/c++draft/intro.compliance#8
轨道上的轻度比赛

@LightnessRaceswithMonica是否有任何语言禁止此扩展?
Mgetz,

@Mgetz抱歉,我不熟悉所有现有语言,所以我无法回答
Lightness Races in Orbit

我不确定是否使用根据本文档格式错误的扩展名。他们添加了一个过载,std::vector<bool>::reference因此实际上没有任何错误。对我来说,听起来好像要使用某种char * foo = "bar";诊断程序,因为它的格式不正确。
NathanOliver
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.