C++ std::initializer_list 作为函数参数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2357452/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
std::initializer_list as function argument
提问by fredoverflow
For some reason I thought C++0x allowed std::initializer_list
as function argument for functions that expect types that can be constructed from such, for example std::vector
. But apparently, it does not work. Is this just my compiler, or will this never work? Is it because of potential overload resolution problems?
出于某种原因,我认为 C++0x 允许std::initializer_list
作为函数参数,用于期望可以从中构造类型的函数,例如std::vector
. 但显然,它不起作用。这只是我的编译器,还是永远不会工作?是因为潜在的重载解析问题吗?
#include <string>
#include <vector>
void function(std::vector<std::string> vec)
{
}
int main()
{
// ok
std::vector<std::string> vec {"hello", "world", "test"};
// error: could not convert '{"hello", "world", "test"}' to 'std::vector...'
function( {"hello", "world", "test"} );
}
回答by Johannes Schaub - litb
GCC has a bug. The Standard makes this valid. See:
GCC 有一个错误。该标准使其有效。看:
Notice that there are two sides of this
请注意,这有两个方面
- How and what initialization is done in general?
- How is initialization used during overload resolution, and what cost does it have?
- 通常如何以及如何完成初始化?
- 在重载解析期间如何使用初始化,它有什么成本?
The first question is answered in section 8.5
. The second question is answered in section 13.3
. For example, reference binding is handled at 8.5.3
and 13.3.3.1.4
, while list initialization is handled in 8.5.4
and 13.3.3.1.5
.
第一个问题在 部分回答8.5
。第二个问题在 部分回答13.3
。例如,引用绑定在8.5.3
and处处理13.3.3.1.4
,而列表初始化在8.5.4
and处处理13.3.3.1.5
。
8.5/14,16
:
8.5/14,16
:
The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization.
.
.
The semantics of initializers are as follows[...]: If the initializer is a braced-init-list, the object is list-initialized (8.5.4).
发生在表单中的初始化
T x = a;
以及在参数传递、函数返回、抛出异常(15.1)、处理异常(15.3)和聚合成员初始化(8.5.1)中称为复制初始化。
.
.
初始化器的语义如下[...]:如果初始化器是花括号初始化器列表,则对象是列表初始化的(8.5.4)。
When considering the candidate function
, the compiler will see an initializer list (which has no type yet - it's just a grammatical construct!) as the argument, and a std::vector<std::string>
as the parameter of function
. To figure out what the cost of conversion is and whether we canconvert these in context of overloading, 13.3.3.1/5
says
在考虑候选 时function
,编译器会看到一个初始化列表(它没有类型 - 它只是一个语法结构!)作为参数,astd::vector<std::string>
作为 的参数function
。要弄清楚转换的成本是多少,以及我们是否可以在重载的情况下转换这些成本,13.3.3.1/5
说
13.3.3.1.5/1
:
13.3.3.1.5/1
:
When an argument is an initializer list (8.5.4), it is not an expression and special rules apply for converting it to a parameter type.
当参数是初始值设定项列表 (8.5.4) 时,它不是表达式,并且特殊规则适用于将其转换为参数类型。
13.3.3.1.5/3
:
13.3.3.1.5/3
:
Otherwise, if the parameter is a non-aggregate class X and overload resolution per 13.3.1.7 chooses a single best constructor of X to perform the initialization of an object of type X from the argument initializer list, the implicit conversion sequence is a user-de?ned conversion sequence. User-de?ned conversions are allowed for conversion of the initializer list elements to the constructor parameter types except as noted in 13.3.3.1.
否则,如果参数是非聚合类 X 并且根据 13.3.1.7 的重载决议选择 X 的单个最佳构造函数来执行参数初始化器列表中类型 X 对象的初始化,则隐式转换序列是用户-定义的转换顺序。除了 13.3.3.1 中的说明外,允许用户定义的转换将初始化列表元素转换为构造函数参数类型。
The non-aggregate class X
is std::vector<std::string>
, and i will figure out the single best constructor below. The last rule grants us to use user defined conversions in cases like the following:
非聚合类X
是std::vector<std::string>
,我将在下面找出单个最佳构造函数。最后一条规则允许我们在以下情况下使用用户定义的转换:
struct A { A(std::string); A(A const&); };
void f(A);
int main() { f({"hello"}); }
We are allowed to convert the string literal to std::string
, even if this needs a user defined conversion. However, it points to restrictions of another paragraph. What does 13.3.3.1
say?
我们可以将字符串文字转换为std::string
,即使这需要用户定义的转换。然而,它指出了另一段的限制。什么13.3.3.1
发言权?
13.3.3.1/4
, which is the paragraph responsible for forbidding multiple user defined conversions. We will only look at list initializations:
13.3.3.1/4
,这是负责禁止多个用户定义转换的段落。我们将只看列表初始化:
However, when considering the argument of a user-de?ned conversion function [(or constructor)] that is a candidate by [...] 13.3.1.7 when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-quali?ed) X is considered for the ?rst parameter of a constructor of X, or [...], only standard conversion sequences and ellipsis conversion sequences are allowed.
但是,当考虑用户定义的转换函数 [(或构造函数)] 的参数时,该函数是 [...] 13.3.1.7 的候选对象,当将初始化列表作为单个参数传递或当初始化列表恰好具有一个元素和对某个类 X 的转换或对(可能是 cv 限定的)X 的引用被视为 X 的构造函数的第一个参数,或 [...],只有标准转换序列和省略号转换序列是允许。
Notice that this is an important restriction: If it weren't for this, the above can use the copy-constructor to establish an equally well conversion sequence, and the initialization would be ambiguous. (notice the potential confusion of "A or B and C" in that rule: It is meant to say "(A or B) and C" - so we are restricted onlywhen trying to convert by a constructor of X having a parameter of type X
).
请注意,这是一个重要的限制:如果不是这个,上面的可以使用复制构造函数来建立一个同样好的转换序列,并且初始化将是模棱两可的。(请注意该规则中“A 或 B 和 C”的潜在混淆:它的意思是“(A 或 B)和 C”-因此我们仅在尝试通过具有参数的 X 构造函数进行转换时受到限制键入X
)。
We are delegated to 13.3.1.7
for collecting the constructors we can use to do this conversion. Let's approach this paragraph from the general side starting from 8.5
which delegated us to 8.5.4
:
我们被委托13.3.1.7
收集我们可以用来进行这种转换的构造函数。让我们从一般的角度来看这一段,从8.5
它委托我们开始8.5.4
:
8.5.4/1
:
8.5.4/1
:
List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initializationand list-initialization in a copy-initialization context is called copy-list-initialization.
列表初始化可以发生在直接初始化或复制初始化上下文中;直接初始化上下文中的列表初始化称为直接列表初始化,复制初始化上下文中的列表初始化称为复制列表初始化。
8.5.4/2
:
8.5.4/2
:
A constructor is an initializer-list constructorif its ?rst parameter is of type
std::initializer_list<E>
or reference to possibly cv-quali?edstd::initializer_list<E>
for some type E, and either there are no other parameters or else all other parameters have default arguments (8.3.6).
一个构造函数是一个初始化列表构造函数,如果它的第一个参数是某种类型 E 的
std::initializer_list<E>
可能 cv 限定的类型或引用std::initializer_list<E>
,并且要么没有其他参数,要么所有其他参数都有默认参数 (8.3.6) .
8.5.4/3
:
8.5.4/3
:
List-initialization of an object or reference of type T is de?ned as follows: [...] Otherwise, if T is a class type, constructors are considered. If T has an initializer-list constructor, the argument list consists of the initializer list as a single argument; otherwise, the argument list consists of the elements of the initializer list. The applicable constructors are enumerated (13.3.1.7) and the best one is chosen through overload resolution (13.3).
T 类型的对象或引用的列表初始化定义如下: [...] 否则,如果 T 是类类型,则考虑构造函数。如果 T 具有初始化列表构造函数,则参数列表由初始化列表作为单个参数组成;否则,参数列表由初始化列表的元素组成。枚举适用的构造函数 (13.3.1.7),并通过重载决议 (13.3) 选择最好的构造函数。
At this time, T
is the class type std::vector<std::string>
. We have one argument (which does not have a type yet! We are just in the context of having a grammatical initializer list). Constructors are enumerated as of 13.3.1.7
:
此时,T
是类类型std::vector<std::string>
。我们有一个参数(它还没有类型!我们只是在有一个语法初始化列表的上下文中)。构造函数被枚举为13.3.1.7
:
[...] If T has an initializer-list constructor (8.5.4), the argument list consists of the initializer list as a single argument; otherwise, the argument list consists of the elements of the initializer list. For copy-list-initialization, the candidate functions are all the constructors of T. However, if an explicit constructor is chosen, the initialization is ill-formed.
[...] 如果 T 具有初始化列表构造函数(8.5.4),则参数列表由初始化列表作为单个参数组成;否则,参数列表由初始化列表的元素组成。对于复制列表初始化,候选函数都是 T 的构造函数。但是,如果选择显式构造函数,则初始化是格式错误的。
We will only consider the initializer list of std::vector
as the only candidate, since we already know the others won't win against it or won't fit the argument. It has the following signature:
我们只会将 的初始化列表std::vector
视为唯一的候选者,因为我们已经知道其他人不会战胜它或不适合该论点。它具有以下签名:
vector(initializer_list<std::string>, const Allocator& = Allocator());
Now, the rules of converting an initializer list to an std::initializer_list<T>
(to categorize the cost of the argument/parameter conversion) are enumerated in 13.3.3.1.5
:
现在,将初始值设定项列表转换为std::initializer_list<T>
(以对参数/参数转换的成本进行分类)的规则列举在13.3.3.1.5
:
When an argument is an initializer list (8.5.4), it is not an expression and special rules apply for converting it to a parameter type. [...] If the parameter type is
std::initializer_list<X>
and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X. This conversion can be a user-de?ned conversioneven in the context of a call to an initializer-list constructor.
当参数是初始值设定项列表 (8.5.4) 时,它不是表达式,并且特殊规则适用于将其转换为参数类型。[...] 如果参数类型是
std::initializer_list<X>
并且初始化器列表的所有元素都可以隐式转换为 X,则隐式转换序列是将列表中的元素转换为 X 所需的最差转换。这种转换可以是用户即使在调用初始化列表构造函数的上下文中也定义了转换。
Now, the initializer list will be successfully converted, and the conversion sequence is a user defined conversion (from char const[N]
to std::string
). How this is made is detailed at 8.5.4
again:
现在,初始化列表将成功转换,转换顺序是用户定义的转换(从char const[N]
到std::string
)。这是如何制作的8.5.4
再次详细说明:
Otherwise, if T is a specialization of
std::initializer_list<E>
, an initializer_list object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type (8.5). (...)
否则,如果 T 是 的特化
std::initializer_list<E>
,则如下所述构造一个 initializer_list 对象,并用于根据从相同类型的类(8.5)初始化对象的规则来初始化对象。(……)
See 8.5.4/4
how this final step is made :)
看看8.5.4/4
这最后一步是如何完成的:)
回答by UncleBens
It seems to work this way:
它似乎是这样工作的:
function( {std::string("hello"), std::string("world"), std::string("test")} );
Perhaps it is a compiler bug, but perhaps you are asking for too many implicit conversions.
也许这是一个编译器错误,但也许您要求太多的隐式转换。
回答by Jerry Coffin
Offhand, I'm not sure, but I suspect what's going on here is that converting to an initializer_list is one conversion, and converting that to vector is another conversion. If that's the case, you're exceeding the limit of only one implicit conversion...
顺便说一句,我不确定,但我怀疑这里发生的事情是转换为 initializer_list 是一种转换,而将其转换为向量是另一种转换。如果是这种情况,您就超出了仅一次隐式转换的限制...
回答by Ricky65
This is either a compiler bug or your compiler doesn't support std::initializer_list. Tested on GCC 4.5.1 and it compiles fine.
这要么是编译器错误,要么是您的编译器不支持 std::initializer_list。在 GCC 4.5.1 上测试过,编译正常。
回答by fnc12
You have to specify type of your initializer_list
您必须指定您的 initializer_list 的类型
function(std::initializer_list<std::string>{"hello", "world", "test"} );
Good luck
祝你好运