通用引用和std :: initializer_list

use*_*017 14 c++ c++11

在他的"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_lists的通用引用的函数,但是当使用auto + { expr } => initializer_list-idiom时,无论是采用initializer_listby值还是by,它都是有用的const&.至少在我看来,这种行为完全令人惊讶.哪种行为符合标准?有谁知道背后的逻辑?

Xeo*_*Xeo 9

这就是关键:从braced-init-list({expr...})中推导出一个类型只对模板参数起作用auto.使用模板参数,您将获得演绎失败,并且不会考虑重载.这导致第一和第三输出.

无论是采用initializer_listby还是by,它都有关系const&

foo:对于任何X,两个重载服用XX&参数不明确为一个左值参数-两者都同样可行的(同样为XVS 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第三子子弹) .