Initializer_list作为非模板上下文中的数组引用参数的参数

时间:2014-12-10 09:43:36

标签: c++ c++11 initializer-list overload-resolution

我的问题涉及这个非常简单和简短的代码,其中在接受数组引用参数的两个非模板函数之间尝试重载解析。该问题已在其他地方发布,但在模板演绎上下文中。这是代码:

#include <iostream>

void foo ( const int (&x) [3] ) { std::cout << "3\n"; }
void foo ( const int (&x) [2] ) { std::cout << "2\n"; }

int main()
{
    foo({1,2,3});
}

g ++ 4.8.3编译此代码选择第一个函数为(我想)唯一可行的, 虽然clang 3.4没有编译它,但是对foo的调用是模棱两可的(为什么?)。

哪种编译器做对了?

clang甚至没有编译代码,甚至删除第二个重载:似乎不接受初始化数组引用的初始化列表。

这辆车有问题吗?

2 个答案:

答案 0 :(得分:6)

我认为虽然GCC的行为更有用,但它的行为对C ++ 11来说是正确的:

  

13.3.3.1.5列表初始化序列[over.ics.list]

     

2如果参数类型为std::initializer_list<X>或&#34;数组为X&#34;并且初始化列表的所有元素都可以隐式转换为X,隐式转换序列是将列表元素转换为X所需的最差转换。

此转换序列不关注数组长度。两个函数重载都提供了一个隐式转换序列,即身份转换:两者都引用int数组,函数参数中的每个元素都是int

重载决策然后会看到两个身份转换,虽然标准确实有一些例外可以解决同等级别转换的冲突,但没有一个能够关注数组的长度:

  

13.3.3.2对隐式转换序列进行排名[over.ics.rank]

     

3两个相同形式的隐式转换序列是无法区分的转换序列,除非符合以下规则之一:

后跟一个完全没有提及数组的列表。

Jonathan Wakely指出,此后发生了变化。您的问题正是促使该更改的原因,相应的DR为#1307。在C ++ 14中,您的代码是有效的,但在C ++ 11中则无效。

答案 1 :(得分:4)

DR 1232改变了C ++ 11,允许从初始化列表中调用带有引用数组参数的函数。 (工作文件的更改显示在N3262中。)所有测试的编译器都实现了该规则。

DR1307然后再次更改相同的措辞以解决您发现的歧义。您的代码被GCC和EDG编译器接受,因此我认为Clang尚未实现该DR。

值得注意的是,即使DR已经解决,foo({1, 2})仍然含糊不清,并被所有编译器拒绝。原因是{1, 2}可以绑定到const int(&)[2]参数(显然),但它也可以绑定到const int(&)[3]参数,因为int是默认可构造的,所以它意味着与{1, 2, int()}相同,即{1, 2, 0}