我原本期望编译以下代码:
#include <set>
using namespace std;
class A {
public:
set<int> st;
A(set<int> s) : st(s) {}
};
int main() {
A a = {1,2,3}; // Error, couldn't convert from initializer list
A b({1,2,3}); // Ok
}
我无法理解为什么第一次施工失败但第二次施工成功。我试图在不使用初始化程序列表的情况下复制错误,但我无法这样做。
我使用的是vc12,但它在microsoft在线可视化编译器上也失败了(我的编译器有不同的错误信息和代码)。
这应该编译吗?
如果不是为什么不呢?
编辑:
以下编译(编辑:仅在Visual Studio中,这是一个错误。):
class A {
public:
int i;
A(int j) : i(i) {}
};
class B {
public:
A a;
B(A o) : a(o) {}
};
class C {
public:
B b;
C(B u) : b(u) {}
};
int main() {
C c = A(10); // Compiles just fine, but isn't this doing the same set of implicit conversions?
C v(A(10));
}
就隐式转换的数量而言,这两个看起来是一样的。
答案 0 :(得分:3)
鉴于此代码:
#include <set>
using namespace std;
class A {
public:
set<int> st;
A(set<int> s) : st(s) {}
};
int main() {
A a = {1,2,3}; // Error, couldn't convert from initializer list
A b({1,2,3}); // Ok
}
使用“a
”语法的=
声明是复制初始化,带有括号括起的列表。
如果A
是普通旧数据类型或“聚合”,则列表中的值将用于按顺序初始化a
项,其余项目如果任何,都将被零初始化。
如果A
改为让构造函数采用合适的std::initializer_list
参数,那么就可以使用它。
这两种可能性是详尽无遗的。例如,如果考虑了std::set
的构造函数(它不被考虑),那么这将涉及两个隐式的用户定义转换,以在{{1的右侧生成临时对象},即=
→std:initializer_list
,然后是std::set
→std:.set
。并且C ++的规则将隐式转换序列限制为最多一个用户定义的转换。
因此,由于类A
既不是POD /聚合也不是具有A
构造的类,因此会出错。
std::initializer_list
的声明是直接初始化。这里提供的参数不用于生成临时b
,而是用于生成某些A
构造函数可接受的参数。找到了一种这样的可能性:使用参数生成A
。
答案 1 :(得分:2)
使用=
初始化对象时,编译器会隐式创建您的类型的临时对象,然后将其复制到您的变量中(尽管临时和副本可能通常是elided)。 / p>
所以你的a
对象在理论上等同于:
A a = A(std::set<int>({1, 2, 3}));
我不确定行为的确切原因,但我认为这是因为允许编译器只隐式执行one user defined conversion。在这种情况下,它可能被认为是两个独立的UDC:
std::set
std::set
至A
答案 2 :(得分:1)
在第一种情况下,您尝试使用构造函数构造对象,该构造函数将std::initializer_list作为单个参数(未实现)。在第二次调用中,构造一个临时std::set<int>
实例,并调用A(set<int> s)
构造函数。
为了解决它,请实现缺少的构造函数:
A(std::initializer_list<int> list) : st(list) { }