use*_*498 6 c++ constructor copy-constructor explicit-constructor
在继续阅读本文之前,请阅读复制初始化和直接初始化之间的C++是否存在差异?首先,确保你理解它在说什么.
我先在这里总结一下规则(阅读标准n3225 8.5/16,13.3.1.3,13.3.1.4和13.3.1.5),
1)对于直接初始化,所有构造函数都将被视为重载集,重载决策将根据重载决策规则选择最佳构造函数.
2)对于复制初始化,源类型与目标类型相同或从目标类型派生,规则与上面相同,只是只转换构造函数(没有显式的构造函数)将被视为重载集.这实际上意味着显式复制/移动构造函数不会被视为重载集.
3)对于上面(2)中未包含的复制初始化情况(源类型与目标类型不同而不是从目标类型派生),我们首先考虑可以从源类型转换为目标类型的用户定义转换序列或(当使用转换函数时)到其派生类.如果转换成功,则结果用于指示初始化目标对象.
3.1)在这个用户定义的转换序列中,根据8.5/16和13.3.1.4中的规则,将考虑转换ctors(非显式ctors)和非显式转换函数.
3.2)结果prvalue将直接初始化目标对象,如(1)中列出的规则,见8.5/16.
好吧,对于规则来说,让我们看看一些奇怪的代码,我真的不知道我的推理错在哪里,或者只是所有编译器都错了.请帮帮我,谢谢.
struct A
{
A (int) { }
A() { }
explicit A(const A&) { }
};
struct B
{
operator A() { return 2; }
//1) visual c++ and clang passes this
//gcc 4.4.3 denies this, says no viable constructor available
};
int main()
{
B b;
A a = b;
//2) oops, all compilers deny this
}
Run Code Online (Sandbox Code Playgroud)
在我的理解中,对于(1),
operator A() { return 2; }
Run Code Online (Sandbox Code Playgroud)
因为C++有一个规则,函数返回被视为复制初始化,根据上面的规则,2将首先隐式转换为A,这应该没问题,因为A有一个构造函数A(int).然后转换后的临时prvalue将用于直接初始化返回的对象,这也应该没问题,因为直接初始化可以使用显式复制构造函数.因此海湾合作委员会错了.
对于(2),
A a = b;
Run Code Online (Sandbox Code Playgroud)
在我的理解中,首先通过运算符A()将b隐式转换为A,然后转换后的值将用于直接初始化a,当然可以调用显式复制构造函数?因此,这应该通过编译,所有编译器都错了?
请注意,对于(2),visual c ++和clang都有类似于"Error,无法从B转换为A"的错误,但是如果我在A的复制构造函数中删除了explicit关键字,则错误消失了.
谢谢阅读.
编辑1
因为有人还没有得到我的意思,我引用了8.5/16的以下标准,
否则(即,对于剩余的复制初始化情况),可以如13.3中所述枚举可以从源类型转换为目的地类型或(当使用转换函数时)到其派生类的用户定义的转换序列. 1.4,通过重载决策(13.3)选择最好的一个.如果转换不能完成或不明确,则初始化是错误的.选择的函数以初始化表达式作为参数调用; 如果函数是构造函数,则调用初始化目标类型的cv-nonqualified版本的临时函数.临时是一个prvalue.然后,根据上面的规则,调用的结果(对于构造函数的情况是临时的)用于直接初始化作为复制初始化目标的对象.在某些情况下,允许实现通过将中间结果直接构造到正在初始化的对象中来消除此直接初始化中固有的复制; 见12.2,12.8.
请注意,它确实提到了在用户定义的转换后直接初始化.这意味着,根据我的理解,下面的代码应遵守我评论的规则,这是由clang,coomeau online,visual c ++确认的,但GCC 4.4.3同时失败了(1)和(2).虽然这是一个奇怪的规则,但它遵循标准的推理.
struct A
{
A (int) { }
A() { }
explicit A(const A&) { }
};
int main()
{
A a = 2; //1)OK, first convert, then direct-initialize
A a = (A)2; //2)oops, constructor explicit, not viable here!
}
Run Code Online (Sandbox Code Playgroud)
你声明了你的拷贝构造函数explicit(BTW,为什么?),这意味着它不能再用于类对象的隐式复制.为了使用此构造函数进行复制,您现在必须使用直接初始化语法.见12.3.1/2
2显式构造函数与非显式构造函数一样构造对象,但仅在显式使用直接初始化语法(8.5)或强制转换(5.2.9,5.4)的情况下才这样做.
这个问题可以通过以下更简短的例子来说明
struct A {
A() {}
explicit A(const A&) {}
};
int main() {
A a;
A b = a; // ERROR: copy-initialization
A c(a); // OK: direct-initialization
}
Run Code Online (Sandbox Code Playgroud)
这就是阻止所有转换工作的原因,因为它们都依赖于复制初始化,而复制初始化又依赖于隐式复制.你禁用了隐式复制.
另外,请参阅涵盖此特定问题的缺陷报告#152.虽然我不确定"提议的决议"的后果应该是什么......