【发布时间】:2013-06-26 08:03:51
【问题描述】:
在他的“C++ 和超越 2012:通用引用”演示文稿中,Scott 反复强调这一点,通用引用 处理/绑定到所有内容,因此重载已经采用通用引用参数的函数确实没有意义。
我没有理由怀疑这一点,直到我将它们与std::initializer_list 混合在一起。
这是一个简短的例子:
#include <iostream>
#include <initializer_list>
using namespace std;
template <typename T>
void foo(T&&) { cout << "universal reference" << endl; }
template <typename T>
void foo(initializer_list<T>) { cout << "initializer list" << endl; }
template <typename T>
void goo(T&&) { cout << "universal reference" << endl; }
template <typename T>
void goo(initializer_list<T> const&) { cout << "initializer list" << endl; }
int main(){
auto il = {4,5,6};
foo( {1,2,3} );
foo( il );
goo( {1,2,3} );
goo( il );
return 0;
}
奇怪的是,VC11 Nov 2012 CTP 抱怨模棱两可 (error C2668: 'foo' : ambiguous call to overloaded function)。更令人惊讶的是,gcc-4.7.2、gcc-4.9.0 和 clang-3.4 同意以下输出:
initializer list
initializer list
initializer list
universal reference
因此,显然可以(使用 gcc 和 clang)重载使用 initializer_lists 进行通用引用的函数,但是在使用 auto + { expr } => initializer_list-idiom 时,无论是通过值还是通过 @ 来获取 initializer_list 都无关紧要987654328@。
至少对我来说,这种行为完全令人惊讶。
哪种行为符合标准?有人知道这背后的逻辑吗?
【问题讨论】:
-
最后一个是[over.ics.rank]/3 sub-bullet 6的一个例子,说明参数类型较少cv-qualified的重载将被明确使用。
-
您确定演示文稿中有您声称的一般性声明吗?确实,通用引用会绑定到任何东西,但重载消歧机制(即对于模板)总是将通用引用列为最具体的重载显然不是真的。
-
略过幻灯片,在第 17 张幻灯片上显示:
Overloading + URef almost always an error. Makes no sense: URefs handle everything. [...] -
@user2523017 好的。用于对模板重载进行排名的规则不容易理解。我可能是错的(尽管 Xeo 的回答似乎同意),但我自然会假设
template <class T> void f(initializer_list<T>)比template <class T> void f(T&&)更专业。使用 GCC 的简单测试似乎也证实了这一点(不一定使用initializer_list作为容器)。 -
@jogojapan
template <class T> void f(initializer_list<T>)确实比template <class T> void f(T&&)更专业,但关键是它不在乎。重载选择首先根据转换顺序进行排名,然后如果还没有最佳重载其他标准包括部分排序(“更专业”)。