简体   繁体   English

std :: initializer_list作为函数参数

[英]std::initializer_list as function argument

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 . 出于某种原因,我认为C ++ 0x允许将std::initializer_list用作可期望可以从中构造类型的函数的函数参数,例如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? 是因为潜在的过载解决问题吗?

#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"} );
}

GCC has a bug. GCC有一个错误。 The Standard makes this valid. 该标准使之有效。 See: 看到:

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 . 第一个问题在8.5节中回答。 The second question is answered in section 13.3 . 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.313.3.3.1.4处理,而列表初始化在8.5.413.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. 以及参数传递 ,函数返回,引发异常(15.1),处理异常(15.3)和聚合成员初始化(8.5.1)都称为复制初始化。
.
.
The semantics of initializers are as follows[...]: If the initializer is a braced-init-list, the object is list-initialized (8.5.4). 初始化程序的语义如下[...]:如果初始化程序是一个括号初始列表,则该对象将进行列表初始化(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 . 在考虑候选function ,编译器将看到一个初始化列表(尚无类型-它只是一个语法构造!)作为参数,而std::vector<std::string>作为function的参数。 To figure out what the cost of conversion is and whether we can convert these in context of overloading, 13.3.3.1/5 says 为了弄清楚转换成本是多少,以及我们是否可以在超载的情况下转换这些成本, 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-defined conversion sequence. 否则,如果参数是非聚合类X,并且按照13.3.1.7的重载分辨率,从参数初始值设定项列表中选择X的最佳最佳构造函数来执行类型X对象的初始化,则隐式转换序列是用户-定义的转换顺序。 User-defined conversions are allowed for conversion of the initializer list elements to the constructor parameter types except as noted in 13.3.3.1. 用户定义的转换允许将初始化列表元素转换为构造函数参数类型,除非13.3.3.1中有说明。

The non-aggregate class X is std::vector<std::string> , and i will figure out the single best constructor below. 非聚合类Xstd::vector<std::string> ,我将在下面找出单个最佳构造函数。 The last rule grants us to use user defined conversions in cases like the following: 最后一条规则允许我们在以下情况下使用用户定义的转换:

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. 即使需要用户定义的转换,我们也可以将字符串文字转换为std::string However, it points to restrictions of another paragraph. 但是,它指出了另一段的限制。 What does 13.3.3.1 say? 13.3.3.1说什么?

13.3.3.1/4 , which is the paragraph responsible for forbidding multiple user defined conversions. 13.3.3.1/4 ,该段负责禁止多个用户定义的转换。 We will only look at list initializations: 我们将仅关注列表初始化:

However, when considering the argument of a user-defined 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-qualified) X is considered for the first parameter of a constructor of X, or [...], only standard conversion sequences and ellipsis conversion sequences are allowed. 但是,当考虑将用户定义的转换函数[(或构造函数)]的参数作为[...] 13.3.1.7的候选对象时,将初始化程序列表作为单个参数传递时,或者当初始化程序列表中只有一个元素时graco.com graco.com X的构造函数的第一个参数考虑转换为某类X或引用(可能是经过cv限定)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. 请注意,这是一个重要的限制:如果不是这样做的话,上面的代码可以使用copy-constructor建立同样良好的转换序列,并且初始化将是不明确的。 (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 only when 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. 我们委托给13.3.1.7来收集可用于执行此转换的构造函数。 Let's approach this paragraph from the general side starting from 8.5 which delegated us to 8.5.4 : 让我们从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-initialization and list-initialization in a copy-initialization context is called copy-list-initialization . 在直接初始化上下文中的列表初始化称为直接列表初始化,而在复制初始化上下文中的列表初始化称为copy-list-initialization

8.5.4/2 : 8.5.4/2

A constructor is an initializer-list constructor if its first parameter is of type std::initializer_list<E> or reference to possibly cv-qualified std::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). 如果构造函数的第一个参数的类型为std::initializer_list<E>或对某些类型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 defined as follows: [...] Otherwise, if T is a class type, constructors are considered. 对象或类型T的引用的列表初始化定义如下:[...]否则,如果T是类类型,则考虑构造函数。 If T has an initializer-list constructor, the argument list consists of the initializer list as a single argument; 如果T具有一个initializer-list构造函数,则参数列表由作为单个参数的初始化器列表组成; 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). 列举了适用的构造函数(13.3.1.7),并通过重载分辨率(13.3)选择了最佳的构造函数。

At this time, T is the class type std::vector<std::string> . 此时, T是类类型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 : 13.3.1.7枚举构造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; [...]如果T具有初始化器列表构造函数(8.5.4),则参数列表由作为单个参数的初始化器列表组成; 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的所有构造函数。但是,如果选择了显式构造函数,则初始化格式不正确。

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. 我们只会将std::vector的初始化列表视为唯一的候选对象,因为我们已经知道其他对象不会胜过它,也不适合该参数。 It has the following signature: 它具有以下签名:

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 : 现在,在13.3.3.1.5中列举了将初始化列表转换为std::initializer_list<T> (以对参数/参数转换的成本进行分类):

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)时,它不是表达式,并且特殊规则适用于将其转换为参数类型。 [...] 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-defined conversion even in the context of a call to an initializer-list constructor. [...]如果参数类型为std::initializer_list<X>并且初始化列表的所有元素都可以隐式转换为X,则隐式转换序列是将列表的元素转换为X所需的最差转换。即使在调用initializer-list构造函数的上下文中, 此转换也可以是用户定义的转换

Now, the initializer list will be successfully converted, and the conversion sequence is a user defined conversion (from char const[N] to std::string ). 现在,初始化列表将成功转换,转换顺序是用户定义的转换(从char const[N]std::string )。 How this is made is detailed at 8.5.4 again: 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如何完成最后一步:)

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. 也许这是一个编译器错误,但是也许您要进行太多的隐式转换。

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. 暂时,我不确定,但是我怀疑这里发生的是将其转换为initializer_list是一次转换,而将其转换为vector是另一次转换。 If that's the case, you're exceeding the limit of only one implicit conversion... 如果是这样,您就超出了一次隐式转换的限制...

This is either a compiler bug or your compiler doesn't support std::initializer_list. 这是编译器错误,或者您的编译器不支持std :: initializer_list。 Tested on GCC 4.5.1 and it compiles fine. 在GCC 4.5.1上测试,编译良好。

You have to specify type of your initializer_list 您必须指定您的initializer_list类型

function(std::initializer_list<std::string>{"hello", "world", "test"} );

Good luck 祝好运

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM