这实际上是含糊的吗?(涉及IT技术)

5

我知道在代码中花括号不仅可以表示initializer_list如果不是初始化列表,花括号括起来的列表是什么?

但是它们应该默认为什么呢?

例如,假设我定义了一个重载函数:

void foo(const initializer_list<int>& row_vector) { cout << size(row_vector) << "x1 - FIRST\n"; }
void foo(const initializer_list<initializer_list<int>>& matrix) { cout << size(matrix) << 'x' << size(*begin(matrix)) << " - SECOND\n"; }

如果我调用foo({ 1, 2, 3 }),会显然地调用第一个。如果我调用foo({ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }),则显然会调用第二个。

但是如果我调用:

foo({ { 1 }, { 2 }, { 3 } })

那些嵌套的括号是int初始化程序还是initializer_list<int>初始化程序? gcc说这是模棱两可的,但如果你将该代码在http://webcompiler.cloudapp.net/上运行,Visual Studio会认为它只是构造了一个initializer_list<int>。谁是正确的?这里应该有一个默认值吗?

2
这是模棱两可的,因为我不知道哪个是正确的。如果人类都无法知道,编译器有什么希望呢? - Mooing Duck
3
int i = {42};std::initializer_list<int> ini = { 42 }; 都是正确的 示例。因此这是有歧义的。 - Jarod42
1
@JonathanMee:不是这样的,你不必指定类型。{{1}}可以匹配init_list<int>,或者init_list<init_list<int>>甚至可以根据上下文匹配到std::array<char,1> - Mooing Duck
4
这是非法的,不是因为没有转换,而是由于语法阻止你在那里编写大括号初始化列表。 - T.C.
1
@Jonathan 不是这样的,那不是真的。 - Barry
显示剩余3条评论
1个回答

6

规则在[over.ics.list]中:

否则,如果参数类型为std :: initializer_list<X>并且初始化列表的所有元素都可以隐式转换为X,则隐式转换序列是将列表中的元素转换为X所需的最差转换,或者如果初始化列表没有元素,则为恒等转换。

在你的两个重载中,必要的最差转换是恒等转换。因此,我们有两个具有身份级别的隐式转换序列。[over.match.best]中有一个规则,它优先使用std :: initializer_list<X>的列表初始化而不是其他替代方案(因此,对于{1}std :: initializer_list<int>优先于int),但是没有什么表明这个规则应该递归应用。

由于没有什么可以消除两个转换序列之间的歧义,所以调用是有歧义的。gcc和clang正确地拒绝了。


我有点好奇微软是否决定,由于它没有指定规则是否递归应用,因此最安全的做法是除非另有说明,否则将其视为递归应用,还是他们搞错了什么。 - Justin Time - Reinstate Monica
我认为这需要一个 CWG 问题,因为递归地应用该规则似乎更有用。 - bogdan
1
@bogdan 我发布了,大家似乎更关注enable_shared_from_this :) - Barry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接