无法使用复制初始化(即 =)构造带有初始化列表的 class

Unable to use copy initialization (i.e =) to construct class with initializer list

我最初希望编译以下代码:

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

就隐式转换的数量而言,两者在我看来是一样的。

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

为了修复它,实现缺少的构造函数:

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

当使用 = 初始化一个对象时,编译器会隐式地创建一个您的类型的临时对象,然后将其复制到您的变量中(尽管临时对象和副本可能并且通常是 elided) .

所以你的 a 对象在理论上等同于:

A a = A(std::set<int>({1, 2, 3}));

我不确定为什么行为会有所不同,但我认为这是因为允许编译器仅隐式执行 one user defined conversion。在这种情况下,它可能被认为是两个独立的 UDC:

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

鉴于此代码:

#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 的声明,使用“=”语法,是一个 复制初始化 和一个 brace-enclosed 列表。

如果 A 是普通旧数据类型或“聚合”,则列表中的值将用于按顺序初始化 a 的项目,其余项目,如果有的话,将是 zero-initialized.

如果 A 有一个构造函数采用合适的 std::initializer_list 参数,那么它就会被使用。

这两种可能性都是详尽无遗的。例如,如果考虑了带有 std::set 的构造函数(未考虑),那么这将涉及两个隐式 user-defined 转换以在 [=12] 的右侧生成临时对象=],即std:initializer_liststd::set,然后std:.setA。并且 C++ 的规则将隐式转换序列限制为最多一个用户定义的转换。

因此,由于 class A 既不是 POD/aggregate 也不是具有 std::initializer_list 构造的 class,您会得到一个错误。


b的声明是直接初始化。这里提供的参数不是用来产生一个临时的 A,而是用来产生一些 A 构造函数可接受的参数。发现了一种这样的可能性:使用参数产生 std::set.