为什么对象的初始化会调用复制构造函数?

8
考虑以下最小工作示例:
#include <atomic>

int main() {
  ::std::atomic<bool> a = false;
}

原子类型的拷贝构造函数和拷贝赋值函数均被明确禁用。然而,这应该调用接受布尔类型参数的构造函数

g++和clang++都指出该行代码试图调用atomic的拷贝构造函数:

$ g++ -std=c++1z a.cpp 
a.cpp: In function ‘int main()’:
a.cpp:4:27: error: use of deleted function ‘std::atomic<bool>::atomic(const std::atomic<bool>&)’
   ::std::atomic<bool> a = false;
                           ^~~~~
$ clang++ -std=c++1z a.cpp 
a.cpp:4:23: error: copying variable of type '::std::atomic<bool>' invokes deleted constructor
  ::std::atomic<bool> a = false;
                      ^   ~~~~~

他们为什么要尝试复制一个原子

也许您的clang版本太旧了。无法重现:https://wandbox.org/permlink/InaivNs3hFjSUuXs - llllllllll
在g++中,似乎已经在7.x版本中得到了修复。 - user7860670
哦,是的,当我使用clang 5.0编译时它可以工作。这似乎是gcc和clang中的编译器错误。 - bitmask
似乎这个编译成了 ::std::atomic<bool> a = ::std::atomic<bool>(false);,它创建了一个新的 std::atomic<bool>(false),然后进行复制。但是当像这样做 ::std::atomic<bool> a ( false ); 时,就可以工作了。编译器能够保证复制省略吗?我认为C++17保证了它。 - user9335240
是的,我也是这么想的,花括号的替代方案是我的解决方法。但我在想为什么编译器会将它编译成这个语句。但看起来这似乎是一个错误。 - bitmask
2
@user9335240 - 这实际上是复制初始化,在任何标准修订中都是如此。C++14也有措辞允许直接初始化。只是在C++17之前,这不是一个硬性要求。 - StoryTeller - Unslander Monica
1个回答

1
它尝试调用复制构造函数,因为其移动构造函数已被隐式删除。
假设我们有一个名为X的类。
struct X
{
    X(const X&) = delete; // user-declared

    X(int)
    {
    }
};

现在,如果你要写

X x = 4;

it would be the same as

X x = X(4); // copy/move into x, see 15.6.1

如果您明确删除了复制构造函数,则不会被隐式声明移动构造函数,因此您将获得编译错误

15.8.1 复制/移动构造函数

[...]

如果类X的定义没有显式声明移动构造函数,则仅当:

  • X没有用户声明的复制构造函数,
  • X没有用户声明的复制赋值运算符,
  • X没有用户声明的移动赋值运算符,且
  • X没有用户声明的析构函数。

[注意:当移动构造函数未被隐式声明或显式提供时,本应调用移动构造函数的表达式可能会调用复制构造函数。 —注解结束]

在C++17中,由于引入了保证的复制省略,这一点发生了变化。
这导致该行与以下内容相同

X x(4);

该代码不依赖于复制或移动构造函数,而是调用X(int)

X一样,std::atomic的复制构造函数也被明确删除,这就是为什么如果没有使用C++17支持编译,您的代码将无法编译的原因。


它意味着同样的事情。在哪里说了这个?据我了解,在 C++ 中,即使在 17 年之前,语法 Type var = value;Type var(value); 的作用是相同的。 - bitmask
按照我所写的标准,它确实如此。 - Axalo
15.8.1 似乎在讨论这些是否被定义。它似乎没有涉及初始化语法的含义(据我所知,注释也没有涉及此问题)。 - bitmask
1
在C++14中,[dcl.init]p15-p17中有这样的说明。 - user743382
1
@bitmask Type var = value; 是一种“复制初始化”的形式,而 Type var(value); 是一种“直接初始化”的形式,这两者一直以来都是不同的。主要区别在于,复制初始化不会使用标记为“explicit”的构造函数,但在 C++17 之前,移动或复制构造函数也参与了复制初始化。 - aschepler
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接