【发布时间】:2018-10-26 03:07:22
【问题描述】:
考虑以下两个类:
class B
{
public:
B() { }
B(const B& b) = delete; //Move ctor not implicitly declared
};
class A
{
public:
A() { }
operator B()
{
return B();
}
};
我明白为什么这段代码编译得很好:
A a;
B b = a;
遵循copy-initialization 的规则,对象“a”被转换为类型 B 的纯右值,并且由于在 C++17 中不再需要复制构造函数,因此没有错误:
如果 T 是一个类类型,并且该类型的 cv 非限定版本 other 不是 T 或派生自 T,或者如果 T 是非类类型,但 type of other 是类类型,用户定义的转换序列 可以从 other 的类型转换为 T(或从 T 派生的类型 如果 T 是类类型并且转换函数可用)是 检查并通过重载决议选择最好的。这 转换的结果,它是一个临时纯右值(直到 C++17)prvalue 表达式 (C++17 起) 如果转换构造函数是 使用,然后用于直接初始化对象。最后一步是 通常优化出来并构造转换的结果 直接在为目标对象分配的内存中,但是 需要适当的构造函数(移动或复制)才能访问 即使它没有被使用。 (直到 C++17)
但是为什么这种直接的列表初始化也会编译呢?
A a;
B b{ a };
我在list-initialization 中找不到任何措辞,说明在这种情况下编译器应尝试将 A 转换为 B。只考虑构造函数的重载决议:
如果前一个阶段没有产生匹配,T的所有构造函数 参与针对一组参数的重载决议 由支撑初始化列表的元素组成,具有限制 只允许非缩小转换
但是在这种情况下复制构造函数被删除了,所以它不应该被重载决议选择吗?
【问题讨论】:
-
要明确,当复制只能通过引用复制语义完成时,不需要复制构造函数,仍然需要深度复制对象。
-
代码
B b{ a.operator B() };会很好:从相同类型的纯右值初始化是通过临时实现到b。 (dcl.init/17.6.1)。但是在B b{a};中,a不是纯右值,所以我认为该子句不适用,因此应该拒绝它(dcl.init/17.6.2 - 应用于构造函数的重载决议) -
复制构造函数没有被使用,我认为编译器已经决定将临时从
return B()具体化为b
标签: c++ initialization language-lawyer c++17