3

有人能告诉我这背后的理论吗?

为什么最后一次调用不编译?

test.cc:在函数'int main()'中:test.cc:15:12:错误:'int'[-fpermissive] test.cc:15:12的初始化器周围的大括号太多:

错误:从 '' 到 'int' [-fpermissive] test.cc:9:6 的无效转换:错误:初始化 'void f(std::initializer_list)' [-fpermissive] test.cc:15:12 的参数 1 :

错误:在需要整数的地方使用了聚合值

我认为c ++ 11或g ++ 4.7在这方面被打破了。谢谢!

#include <initializer_list>

class A { 
  public:
  A(const std::initializer_list<int>) {}
};

void f(const std::initializer_list<int>) {}

int main() {
  A({1});  // Compile OK
  f({1});  // Compile OK
  A({{{1}}});   // Compile OK
  //f({{{1}}}); // Compile Error.
}
4

2 回答 2

2

这就是我认为 GCC 的想法。

这是你的程序,多出 1 行,有趣的行被编号。

int main() {
  A({1});  // 1. Compile OK
  f({1});  // 2. Compile OK
  A{{1}};  // 3. Compile OK, equivalent to 1. 
  A({{{1}}});   // 4. Compile OK
  //f({{{1}}}); // 5. Compile Error.
}

为什么 GCC 编译 4 而不是 5?

为清楚起见,假设 #4 的构造实际上声明了一些东西:

A a{{{1}}};   // 4a. Compile OK

GCC 询问构造函数的参数是否{{1}}可以隐式转换为A. 也是这样:

A{{1}}

{{1}}从到的有效转换A?是的 - 根据 3。

当然,这个推理不适用于#5;因此错误。

如果要阻止 GCC 接受 #4,则通过显式启用构造函数来阻止启用转换:

class A { 
    public:
    explicit A(const std::initializer_list<int> il) {}
};

然后#4将给出错误:

error: converting to ‘A’ from initializer list would use explicit constructor ‘A::A(std::initializer_list<int>)’
于 2013-04-07T08:15:03.903 回答
2

{1} 可以初始化一个 int。{{1}} 可能不应该 - 委员会对此有缺陷报告。GCC 禁止这样做,而 clang 目前只是倾向于发出有关冗余大括号的警告。

当 X 是具有复制或移动构造函数的类时,X ({...}) 可以调用其中一个。请注意, X {...} 也可以,但仅限于不允许用户定义的转换(对于复制或移动构造函数)。

现在使用您的 A ({{{1}}}),第一个大括号被复制/移动构造函数使用。第二个递归地进入初始化列表。第三个进入包含的 int。

根据标准,再添加一个大括号将破坏 A ({{{{1}}}})。因为第二个大括号需要由 A 的复制/移动构造函数使用,但需要用户定义的转换序列。A {{{{1}}}} 也是如此,因此也无效。

于 2013-04-07T08:42:03.477 回答