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

Why do the C++11 std::initializer_list constructor overloading rules exist?

本文关键字:list 构造函数 重载 规则 initializer 存在 C++11 std 为什么      更新时间:2023-10-16

我似乎想不出也找不到以下代码的理由:

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}std::vector<int> a{{1,2}} .但是,至少对我来说,这肯定不会超过这带来的歧义和过载分辨率的变化。根据Scott Meyers在Effective Modern C++中的说法,std::make_uniquestd::make_shared需要明确说明哪种形式用于构造作为其接口的一部分(因为过载分辨率)。这对我来说似乎很荒谬。

承认我一定错过了什么,但我只是不确定它是什么。请注意,我只是以std::vector为例,我询问的是一般的功能。

这在 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;
}