无法使用复制初始化(= =)来构造具有初始化列表

时间:2016-01-18 11:47:32

标签: c++ visual-c++ std

我原本期望编译以下代码:

#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));
}

就隐式转换的数量而言,这两个看起来是一样的。

3 个答案:

答案 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::setstd:.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:

  1. 初始化列表到std::set
  2. std::setA

答案 2 :(得分:1)

在第一种情况下,您尝试使用构造函数构造对象,该构造函数将std::initializer_list作为单个参数(未实现)。在第二次调用中,构造一个临时std::set<int>实例,并调用A(set<int> s)构造函数。

为了解决它,请实现缺少的构造函数:

A(std::initializer_list<int> list) : st(list) {  }