Ric*_*ich 14 c++ copy-initialization c++11 list-initialization c++14
鉴于以下内容:
#include <stdio.h>
class X;
class Y
{
public:
Y() { printf(" 1\n"); } // 1
// operator X(); // 2
};
class X
{
public:
X(int) {}
X(const Y& rhs) { printf(" 3\n"); } // 3
X(Y&& rhs) { printf(" 4\n"); } // 4
};
// Y::operator X() { printf(" operator X() - 2\n"); return X{2}; }
int main()
{
Y y{}; // Calls (1)
printf("j\n");
X j{y}; // Calls (3)
printf("k\n");
X k = {y}; // Calls (3)
printf("m\n");
X m = y; // Calls (3)
printf("n\n");
X n(y); // Calls (3)
return 0;
}
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.现在,如果我启用转换运算符Y::operator X()
,我会得到这个; -
X m = y; // Calls (2)
Run Code Online (Sandbox Code Playgroud)
我的理解是发生这种情况是因为(2)比(3)"更少const",因此是首选.对X
构造函数的调用被省略了
我的问题是,为什么定义不会X k = {y}
以同样的方式改变其行为?我知道这= {}
在技术上是"列表复制初始化",但是在没有构造函数采用initializer_list
类型的情况下,这不会恢复为"复制初始化"行为吗?即 - 与...相同X m = y
我的理解在哪里?
我的理解在哪里?
tltldr; 没有人理解初始化.
tldr; 列表初始化更喜欢std::initializer_list<T>
构造函数,但它不会回退到非列表初始化.它只会回到考虑构造函数.非列表初始化将考虑转换函数,但回退不会.
所有初始化规则都来自[dcl.init].所以,让我们从第一原则出发.
- 如果初始化程序是(非括号)braced-init-list或is = braced-init-list,则对象或引用是列表初始化的.
第一个第一个要点包括任何列表初始化.这种跳跃X x{y}
和X x = {y}
到[dcl.init.list] .我们会回过头来看看.另一种情况比较容易.我们来看看吧X x = y
.我们直接打电话给:
- 否则(即,对于剩余的复制初始化情况),用户定义的转换序列可以从源类型转换为目标类型或(当使用转换函数时)到其派生类,如[over]中所述进行枚举.match.copy],通过重载决策选择最好的一个.match.copy].
[over.match.copy]中的候选人是:
T
[在我们的例子中X
] 的转换构造函数是候选函数.- 当初始化表达式的类型是类类型" cv
S
"时,将S
考虑其非基本类的非显式转换函数.在这两种情况下,参数列表都有一个参数,它是初始化表达式.
这给了我们候选人:
X(Y const &); // from the 1st bullet
Y::operator X(); // from the 2nd bullet
Run Code Online (Sandbox Code Playgroud)
第二个相当于有一个X(Y& )
,因为转换函数不是cv合格的.这使得比转换构造函数更少的cv限定引用,因此它是首选.注意,X(X&& )
在C++ 17中没有调用.
现在让我们回到列表初始化案例.第一个相关的要点是[dcl.init.list] /3.6:
否则,如果
T
是类类型,则考虑构造函数.枚举适用的构造函数,并通过重载决策([over.match],[over.match.list])选择最佳构造函数.如果转换任何参数需要缩小转换(见下文),则程序格式错误.
在两种情况下都将我们带到[over.match.list],它定义了两阶段重载决策:
- 最初,候选函数是类T的初始化列表构造函数([dcl.init.list]),参数列表由初始化列表作为单个参数组成.
- 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,参数列表由初始化列表的元素组成.
如果初始化列表没有元素且T具有默认构造函数,则省略第一个阶段.在copy-list-initialization中,如果选择了显式构造函数,则初始化是错误的.
候选人是...的建设者X
.X x{y}
和之间的唯一区别X x = {y}
是,如果后者选择explicit
构造函数,则初始化是错误的.我们甚至没有任何explicit
构造函数,因此两者是等价的.因此,我们枚举我们的构造函数:
X(Y const& )
X(X&& )
通过 Y::operator X()
前者是直接引用绑定,是完全匹配.后者需要用户定义的转换.因此,X(Y const& )
在这种情况下我们更喜欢.
请注意,gcc 7.1在C++ 1z模式下出错,所以我提交了错误80943.
归档时间: |
|
查看次数: |
428 次 |
最近记录: |