为什么在参数中使用initializer_list而不是vector?

时间:2020-12-20 11:00:22

What is the actual benefit and purpose of initializer_list, for unknown number of parameters? Why not just use vector and be done with it?

对于未知数量的参数,initializer_list的实际好处和目的是什么?为什么不使用矢量并完成它?

In fact, it sounds like just a vector with another name. Why bother?

事实上,它听起来只是一个带有另一个名字的矢量。何必?

The only "benefit" I see of initializer_list is that it has const elements, but that doesn't seem to be a reason enough to invent this whole new type. (You can just use a const vector after all.)

我看到的initializer_list唯一的“好处”是它有const元素,但这似乎不足以发明这个全新的类型。 (毕竟你可以使用const向量。)

So, what am I mising?

那么,我在做什么?

3 个解决方案

#1


26  

It is a sort of contract between the programmer and the compiler. The programmer says {1,2,3,4}, and the compiler creates an object of type initializer_list<int> out of it, containing the same sequence of elements in it. This contract is a requirement imposed by the language specification on the compiler implementation.

它是程序员和编译器之间的一种契约。程序员说{1,2,3,4},编译器创建一个类型为initializer_list 的对象,其中包含相同的元素序列。此契约是语言规范对编译器实现强加的要求。

That means, it is not the programmer who creates manually such an object but it is the compiler which creates the object, and pass that object to function which takes initializer_list<int> as argument.

这意味着,不是程序员手动创建这样的对象,而是编译器创建对象,并将该对象传递给以initializer_list 为参数的函数。

The std::vector implementation takes advantage of this contract, and therefore it defines a constructor which takes initializer_list<T> as argument, so that it could initialize itself with the elements in the initializer-list.

std :: vector实现利用了这个契约,因此它定义了一个构造函数,它将initializer_list 作为参数,以便它可以使用initializer-list中的元素初始化自身。

Now suppose for a while that the std::vector doesn't have any constructor that takes std::initializer_list<T> as argument, then you would get this:

现在假设std :: vector没有任何构造函数将std :: initializer_list 作为参数,那么你会得到这个:

 void f(std::initializer_list<int> const &items);
 void g(std::vector<int> const &items);

 f({1,2,3,4}); //okay
 g({1,2,3,4}); //error (as per the assumption)

As per the assumption, since std::vector doesn't have constructor that takes std::initializer_list<T> as argument, which implies you cannot pass {1,2,3,4} as argument to g() as shown above, because the compiler cannot create an instance of std::vector out of the expression {1,2,3,4} directly. It is because no such contract is ever made between programmer and the compiler, and imposed by the language. It is through std::initializer_list, the std::vector is able to create itself out of expression {1,2,3,4}.

根据假设,由于std :: vector没有将std :: initializer_list 作为参数的构造函数,这意味着你不能将{1,2,3,4}作为参数传递给g(),如上所示,因为编译器无法直接从表达式{1,2,3,4}中创建std :: vector的实例。这是因为程序员和编译器之间没有这样的合同,并且由语言强加。通过std :: initializer_list,std :: vector能够从表达式{1,2,3,4}中创建自己。

Now you will understand that std::initializer_list can be used wherever you need an expression of the form of {value1, value2, ...., valueN}. It is why other containers from the Standard library also define constructor that takes std::initializer_list as argument. In this way, no container depends on any other container for construction from expressions of the form of {value1, value2, ...., valueN}.

现在您将了解std :: initializer_list可以在需要{value1,value2,....,valueN}形式的表达式的任何地方使用。这就是为什么Standard库中的其他容器也定义了将std :: initializer_list作为参数的构造函数。通过这种方式,没有容器依赖于{value1,value2,....,valueN}形式的表达式来构造任何其他容器。

Hope that helps.

希望有所帮助。

#2


13  

Well, std::vector has to use initializer_list to get that syntax as it obviously can't use itself.

好吧,std :: vector必须使用initializer_list来获取该语法,因为它显然无法使用它自己。

Anyway, initializer_list is intended to be extremely lightweight. It can use an optimal storage location and prevent unnecessary copies. With vector, you're always going to get a heap allocation and have a good chance of getting more copies/moves than you want.

无论如何,initializer_list旨在非常轻量级。它可以使用最佳存储位置并防止不必要的副本。使用vector,您总是可以获得堆分配,并且很有可能获得比您想要的更多的副本/移动。

Also, the syntax has obvious differences. One such thing is template type deduction:

此外,语法有明显的差异。一个这样的事情是模板类型推导:

struct foo {
    template<typename T>  
    foo(std::initializer_list<T>) {}
};

foo x{1,2,3}; // works

vector wouldn't work here.

矢量在这里不起作用。

#3


9  

The biggest advantage of initializer_list over vector is that it allows you to specify in-place a certain sequence of elements without requiring dedicate processing to create that list.

initializer_list优于矢量的最大优点是它允许您就地指定一定数量的元素,而无需专门处理来创建该列表。

This saves you from setting up several calls to push_back (or a for cycle) for initializing a vector even though you know exactly which elements are going to be pushed into the vector.

这样可以避免设置多个push_back(或for循环)调用来初始化向量,即使您确切知道哪些元素将被推入向量。

In fact, vector itself has a constructor accepting an initializer_list for more convenient initialization. I would say the two containers are complementary.

实际上,vector本身有一个构造函数接受initializer_list以便更方便的初始化。我会说这两个容器是互补的。

// v is constructed by passing an initializer_list in input
std::vector<std::string> v = {"hello", "cruel", "world"};

Of course it is important to be aware of the fact that initializer_list does have some limitations (narrowing conversions are not allowed) which may make it inappropriate or impossible to use in some cases.

当然,重要的是要注意initializer_list确实有一些限制(不允许缩小转换)这一事实,这可能使得在某些情况下使用它不合适或不可能。

#1


26  

It is a sort of contract between the programmer and the compiler. The programmer says {1,2,3,4}, and the compiler creates an object of type initializer_list<int> out of it, containing the same sequence of elements in it. This contract is a requirement imposed by the language specification on the compiler implementation.

它是程序员和编译器之间的一种契约。程序员说{1,2,3,4},编译器创建一个类型为initializer_list 的对象,其中包含相同的元素序列。此契约是语言规范对编译器实现强加的要求。

That means, it is not the programmer who creates manually such an object but it is the compiler which creates the object, and pass that object to function which takes initializer_list<int> as argument.

这意味着,不是程序员手动创建这样的对象,而是编译器创建对象,并将该对象传递给以initializer_list 为参数的函数。

The std::vector implementation takes advantage of this contract, and therefore it defines a constructor which takes initializer_list<T> as argument, so that it could initialize itself with the elements in the initializer-list.

std :: vector实现利用了这个契约,因此它定义了一个构造函数,它将initializer_list 作为参数,以便它可以使用initializer-list中的元素初始化自身。

Now suppose for a while that the std::vector doesn't have any constructor that takes std::initializer_list<T> as argument, then you would get this:

现在假设std :: vector没有任何构造函数将std :: initializer_list 作为参数,那么你会得到这个:

 void f(std::initializer_list<int> const &items);
 void g(std::vector<int> const &items);

 f({1,2,3,4}); //okay
 g({1,2,3,4}); //error (as per the assumption)

As per the assumption, since std::vector doesn't have constructor that takes std::initializer_list<T> as argument, which implies you cannot pass {1,2,3,4} as argument to g() as shown above, because the compiler cannot create an instance of std::vector out of the expression {1,2,3,4} directly. It is because no such contract is ever made between programmer and the compiler, and imposed by the language. It is through std::initializer_list, the std::vector is able to create itself out of expression {1,2,3,4}.

根据假设,由于std :: vector没有将std :: initializer_list 作为参数的构造函数,这意味着你不能将{1,2,3,4}作为参数传递给g(),如上所示,因为编译器无法直接从表达式{1,2,3,4}中创建std :: vector的实例。这是因为程序员和编译器之间没有这样的合同,并且由语言强加。通过std :: initializer_list,std :: vector能够从表达式{1,2,3,4}中创建自己。

Now you will understand that std::initializer_list can be used wherever you need an expression of the form of {value1, value2, ...., valueN}. It is why other containers from the Standard library also define constructor that takes std::initializer_list as argument. In this way, no container depends on any other container for construction from expressions of the form of {value1, value2, ...., valueN}.

现在您将了解std :: initializer_list可以在需要{value1,value2,....,valueN}形式的表达式的任何地方使用。这就是为什么Standard库中的其他容器也定义了将std :: initializer_list作为参数的构造函数。通过这种方式,没有容器依赖于{value1,value2,....,valueN}形式的表达式来构造任何其他容器。

Hope that helps.

希望有所帮助。

#2


13  

Well, std::vector has to use initializer_list to get that syntax as it obviously can't use itself.

好吧,std :: vector必须使用initializer_list来获取该语法,因为它显然无法使用它自己。

Anyway, initializer_list is intended to be extremely lightweight. It can use an optimal storage location and prevent unnecessary copies. With vector, you're always going to get a heap allocation and have a good chance of getting more copies/moves than you want.

无论如何,initializer_list旨在非常轻量级。它可以使用最佳存储位置并防止不必要的副本。使用vector,您总是可以获得堆分配,并且很有可能获得比您想要的更多的副本/移动。

Also, the syntax has obvious differences. One such thing is template type deduction:

此外,语法有明显的差异。一个这样的事情是模板类型推导:

struct foo {
    template<typename T>  
    foo(std::initializer_list<T>) {}
};

foo x{1,2,3}; // works

vector wouldn't work here.

矢量在这里不起作用。

#3


9  

The biggest advantage of initializer_list over vector is that it allows you to specify in-place a certain sequence of elements without requiring dedicate processing to create that list.

initializer_list优于矢量的最大优点是它允许您就地指定一定数量的元素,而无需专门处理来创建该列表。

This saves you from setting up several calls to push_back (or a for cycle) for initializing a vector even though you know exactly which elements are going to be pushed into the vector.

这样可以避免设置多个push_back(或for循环)调用来初始化向量,即使您确切知道哪些元素将被推入向量。

In fact, vector itself has a constructor accepting an initializer_list for more convenient initialization. I would say the two containers are complementary.

实际上,vector本身有一个构造函数接受initializer_list以便更方便的初始化。我会说这两个容器是互补的。

// v is constructed by passing an initializer_list in input
std::vector<std::string> v = {"hello", "cruel", "world"};

Of course it is important to be aware of the fact that initializer_list does have some limitations (narrowing conversions are not allowed) which may make it inappropriate or impossible to use in some cases.

当然,重要的是要注意initializer_list确实有一些限制(不允许缩小转换)这一事实,这可能使得在某些情况下使用它不合适或不可能。