为什么存在C ++ 11 std :: initializer_list构造函数重载规则?

时间:2015-02-02 02:09:10

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

我似乎无法想到也没有找到以下代码的理由:

std::vector<int> a{1,2} // calls (7)
std::vector<int> a(1,2) // calls (2)

// constructors from http://en.cppreference.com/w/cpp/container/vector/vector
vector( std::initializer_list<T> init, 
        const Allocator& alloc = Allocator() ); // (7)

explicit vector( size_type count, 
                 const T& value = T(),
                 const Allocator& alloc = Allocator()); // (2)

根据您使用的构造方法({} vs ())调用不同的函数,这对我来说似乎非常不正常。为什么std::initializer_list优先选择其他与给定参数完全匹配的函数? 我知道上面的构造函数2在C ++ 11中已被弃用,大概是因为这个改变,但我仍然无法推理为什么这是的情况。我可以看到这种行为的唯一好处是你可以初始化一个具有特定值的容器,只需要一对大括号; std::vector<int> a{1,2} vs std::vector<int> a{{1,2}}。但是,至少对我而言,这肯定不会超过这种模糊性以及对此所施加的重载决策的变化。根据Scott Meyers的Effective Modern C ++,std::make_uniquestd::make_shared需要明确说明哪个表单用于构造作为其接口的一部分(因为重载决策)。这对我来说似乎很荒谬。

我承认我必须遗漏一些东西,但我不确定它是什么。请注意,我只是以std::vector为例,我一般都在询问这个功能。

1 个答案:

答案 0 :(得分:3)

这对C ++ 11来说非常有趣:原因是支撑的参数没有类型。但是有一个例外,它存在的原因对我来说并不清楚:“auto”变量是唯一允许将支持的参数隐式地作为初始化列表处理的变量。但如果您有自动函数类型,则不允许返回此初始化列表。

现在,您说得对:初始化程序列表的好处是您可以初始化具有特定值的容器。这是一个值得改变的巨大好处!

在初始化列表之前,制作一个模板,允许您初始化具有不同值的容器内的每个类需要严格的解决方案,例如接收带有每个值的std :: vector,或者构造“空”模板类,以及打孔之后的每个值。

另一个原因是初始化列表允许您以比使用从C导入的可怕模板<cstdarg>更安全的方式为C ++创建可变参数函数。(尽管可变参数模板在此方面做得更好)

想要玩一点组合吗?

#include <iostream>
#include <vector>
#include <typeinfo>

using namespace std;


vector<int> func (const vector<int> &a) { //works
//auto func (const vector<int> &a) -> vector<int> { //works
//auto func (const vector<int> &a) { //don't even compile, "returns a initializer list" error
    for (int i: a) {
        cout << "My " << i << endl;
    }

    return {20 , 30};
}

int main()
{
    //play with anonymous functions
    auto y = [ ](vector<int> e) { return e; }; //works
    vector<int> x = y({20, 30});
    //auto y = [ ](){ return {20, 30}; }; //don't even compile, "returns a initializer list" error
    //vector<int> x = y();

    //play with initialization
    //vector<int> x = {2,2,20,30}; //works
    //vector<int> x{2,2,20,30}; //works
    //auto x = vector<int>{2,2,20,30}; //works
    //Bellow, a common mistake of people initializing a int to a auto, like auto x = { 1 }
    //auto x = {2,2,20,30}; //wrong, but compiles, its a initializer list
    //auto x{2,2,20,30}; //wrong, but compiles, its a initializer list

    //Play with return types
    //vector<int> x = func(vector<int>(2,2));  //works only with vector<int> and auto with trailing type
    //vector<int> x(func(vector<int>(2,2))); //works only with vector<int> and auto with trailing type
    //vector<int> x{func(vector<int>(2,2))}; //works only with vector<int> and auto with trailing type
    //auto x = func(vector<int>(2,2));  //works only with vector<int> and auto with trailing type
    //auto x(func(vector<int>(2,2))); //works only with vector<int> and auto with trailing type

    cout << typeid(x).name() << endl;
    for (int i: x) {
        cout << "My " << i << endl;
    }

    return 0;
}