提问人:R_Kapp 提问时间:6/22/2018 最后编辑:T.C.R_Kapp 更新时间:6/22/2018 访问量:352
复制构造函数是始终隐式定义,还是仅在使用时定义?
Are copy constructors defined implicitly always, or only when they are used?
问:
请考虑以下代码:
#include <memory>
#include <vector>
class A
{
private:
std::vector<std::unique_ptr<int>> _vals;
};
int main()
{
A a;
//A a2(a);
return 0;
}
编译器 A 编译它没有问题,除非我取消注释掉该行,此时它抱怨复制构造函数被删除,因此我无法复制构造。然而,编译器 B 提出这种抱怨,即使我把那一行注释掉了。也就是说,编译器 A 仅在我实际尝试使用它时生成隐式定义的复制构造函数,而编译器 B 则无条件地这样做。哪一个是正确的?请注意,如果我正确地使用而不是两个编译器,则隐式删除复制构造函数和赋值运算符(具有显式删除的复制构造函数,而没有)。A a2(a);
std::unique_ptr
A
std::unique_ptr<int> _vals;
std::vector<std::unique_ptr<int>> _vals;
std::unique_ptr
std::vector
(注意:在编译器 B 中编译代码非常简单 - 只需显式删除复制构造函数和赋值运算符,它就可以正常工作。这不是问题的重点;这是为了理解正确的行为。
答:
虽然我无法确认这种行为(我无法访问 Windows 编译器,并且 OP 声称该错误发生在 Windows 平台上的 icc 上),但从表面上看问题,答案是 - 编译器 B 有一个严重的错误。
特别是,隐式声明的复制构造函数被定义为已删除,当...
T 具有无法复制的非静态数据成员(已删除、 无法访问或模棱两可的复制构造函数);
https://en.cppreference.com/w/cpp/language/copy_constructor
因此,符合要求的编译器必须在语义上生成已删除的复制构造函数,并成功编译程序,因为该构造函数从未调用过。
评论
std::vector
不过,是可复制的。正如我在问题中提到的,如果我替换为 ,我不会遇到问题 - 不可复制,因此复制构造函数被正确隐式删除。,不过,这是编译器所具有的,是可复制的。std::vector<std::unique_ptr<int>> _vals
std::unique_ptr<int> _vals
std::unique_ptr
std::vector
std::vector
std::unique_ptr
std::vector
std::vector<std::unique_ptr<int>>
std::vector<T>
定义了复制构造函数。它不会被删除、无法访问或模棱两可。碰巧的是,它在本例中使用的模板参数 () 是不可复制的,但这不会改变std::unique_ptr
std::vector
当 odr 使用 ([basic.def.odr])、常量求值需要 ([expr.const]) 或在第一次声明后显式默认时,默认且未定义为已删除的复制/移动构造函数是隐式定义的。
A
的复制构造函数是默认的,因此仅当它被 ODR 使用时才会隐式定义。 就是这样一个 ODR 用法 - 所以正是该语句触发了它的定义,这将使程序格式不正确。在使用 odr 之前,不应定义它。A a2(a);
编译器 B 拒绝程序是错误的。
评论
注意:我的回答是基于您的评论:
[...]它仅在 Windows 上,并且只有当我将类 A 明确列为 DLL 导出(例如,通过类 __declspec(dllexport) A))时才会发生这种情况。[...]
在 MSDN 上,我们可以了解到,声明一个类会导出所有成员,并且需要为所有成员定义。我怀疑编译器会生成所有非 d 函数的定义以符合此规则。dllexport
delete
正如你在这里读到的,实际上,我希望假定的机制(在你的案例中定义复制构造函数以用于导出目的)检查这个特征的值(或使用类似的机制),而不是实际检查它是否会编译。这可以解释为什么当你使用而不是 .std::is_copy_constructible<std::vector<std::unique_ptr<int>>>::value
true
unique_ptr<T>
vector<unique_ptr<T>>
因此,问题在于,即使它不会编译,它实际上也定义了复制构造函数。std::vector
恕我直言,检查就足够了,因为在您发生的时候,您无法知道隐式函数是否会在您使用的地方(甚至可能是另一个项目)使用 odr。因此,我不会将其视为编译器中的错误。is_copy_constructible
dllexport
dllimport
B
评论
dll
Y
X
A
std::vector
评论
__declspec(dllexport)