C++ 初始值设定项\u列表作为非模板上下文中数组引用参数的参数

C++ 初始值设定项\u列表作为非模板上下文中数组引用参数的参数,c++,c++11,initializer-list,overload-resolution,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()

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

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

void foo(const int(&x)[3]){std::cout我认为虽然GCC的行为更有用,但对于C++11来说,clang的行为才是正确的:

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

2如果参数类型为
std::initializer_list
或“array of
X
,并且可以将初始值设定项列表的所有元素隐式转换为
X
,则隐式转换序列是将列表元素转换为
X
所需的最差转换

此转换序列不考虑数组长度。两个函数重载都提供了一个隐式转换序列,该序列是一个标识转换:都引用了
int
数组,函数参数中的每个元素都是
int

重载解析会看到两个标识转换,虽然标准中有一些例外情况用于解决相等秩转换上的冲突,但没有一个注意数组的长度:

13.3.3.2隐式转换序列排序[over.ics.rank]

3除非下列规则之一适用,否则相同形式的两个隐式转换序列是不可区分的转换序列:

后面是一个根本没有提到数组的列表

Jonathan Wakely指出,这一点已经发生了变化。您的问题正是导致这种变化的原因,而相应的DR是。在C++14中,您的代码是有效的,但在C++11中无效。

更改了C++11,以允许从初始值设定项列表中引用数组参数调用函数。(对工作文件的更改如所示。)所有经过测试的编译器都实现了这一规则

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


有趣的是,即使在DR被解决之后,
foo({1,2})
仍然是不明确的,并且被所有编译器拒绝。原因是
{1,2}
可以绑定到
常量int(&)[2]
参数(显然),但它也可以绑定到
常量int(&)[3]
参数,因为
int
是默认可构造的,所以它的意思与
{1,2,int()}
相同,即
{1,2,0}

“clang即使删除第二个重载也不会编译代码”--对我来说是这样的(如果第二个重载按照您所说的删除了),使用clang 3.4.2,只要命令行选项中包含
-std=c++11
。哪个版本和哪个调用失败?你是对的:在删除第二个重载后,我编译代码时忘记了标记-std=c++11。很抱歉。现在我定义了一个命令别名,以避免将来出现此类错误…参数type不是
std::initializer_list
,当前的工作文件在不同的段落中分别处理数组和
initializer_list
,特别提到数组长度:“否则,如果参数类型为“nx数组”,如果初始值设定项列表正好有N个元素,或者如果它少于N个元素,并且X是默认可构造的,并且如果初始值设定项列表的所有元素都可以隐式转换为X,则隐式转换序列是将列表中的一个元素转换为X所需的最差转换“
std::initializer\u list
或“array of
X
”,参数类型是数组。我会检查最近论文的措辞。@JonathanWakely Heh,我自己也发现了这一点。:“
foo({1,2})
仍然不明确”——你确定吗?不确定吗“列表初始化序列L1是比列表初始化序列L2更好的转换序列,如果……L1转换为类型“N1 T的数组”,L2转换为类型“N2 T的数组”,并且N1小于N2。”解决此问题有利于
const int(&)[2]
重载?