在他的"C++和Beyond 2012:Universal References"演示中,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;
}
Run Code Online (Sandbox Code Playgroud)
奇怪的是,VC11 2012年11月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
Run Code Online (Sandbox Code Playgroud)
所以显然有可能(使用gcc和clang)重载带有initializer_list
s的通用引用的函数,但是当使用auto + { expr } => initializer_list
-idiom时,无论是采用initializer_list
by值还是by,它都是有用的const&
.至少在我看来,这种行为完全令人惊讶.哪种行为符合标准?有谁知道背后的逻辑?
这就是关键:从braced-init-list({expr...}
)中推导出一个类型只对模板参数起作用auto
.使用模板参数,您将获得演绎失败,并且不会考虑重载.这导致第一和第三输出.
无论是采用
initializer_list
by还是by,它都有关系const&
foo
:对于任何X
,两个重载服用X
和X&
参数不明确为一个左值参数-两者都同样可行的(同样为X
VS X&&
为右值).
struct X{};
void f(X);
void f(X&);
X x;
f(x); // error: ambiguous overloads
Run Code Online (Sandbox Code Playgroud)
然而,部分排序规则步骤在这里(§14.5.6.2),并采取了通用的功能std::initializer_list
是更专业的比普通的一个秋毫.
goo
:对于两个重载用X&
和X const&
参数和X&
参数,第一个是更可行的,因为所述第二过载需要一个资格转换从X&
到X const&
(§13.3.3.1.2/ 1表12和§13.3.3.2/ 3第三子子弹) .