出于某种原因,我认为 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"} );
}
最佳答案
GCC 有一个错误。该标准使这一点有效。请参阅:
请注意,这有两个方面
- 一般如何以及进行哪些初始化?
- 在重载解析期间如何使用初始化,它的成本是多少?
第一个问题在 8.5
部分中得到解答。第二个问题在13.3
部分得到解答。 。例如,引用绑定(bind)在 8.5.3
处处理。和13.3.3.1.4
,而列表初始化在 8.5.4
中处理和13.3.3.1.5
。
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).
在考虑候选人时function
,编译器将看到一个初始值设定项列表(它还没有类型 - 它只是一个语法构造!)作为参数,以及 std::vector<std::string>
作为function
的参数。要弄清楚转换成本是多少以及我们是否可以在重载的情况下转换这些成本,13.3.3.1/5
说
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.
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. 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.
非聚合类X
是 std::vector<std::string>
,我将在下面找出最好的构造函数。最后一条规则允许我们在以下情况下使用用户定义的转换:
struct A { A(std::string); A(A const&); };
void f(A);
int main() { f({"hello"}); }
我们可以将字符串文字转换为 std::string
,即使这需要用户定义的转换。然而,它指出了另一段的限制。 13.3.3.1
是什么意思?说什么?
13.3.3.1/4
,这是负责禁止多个用户定义转换的段落。我们只会查看列表初始化:
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.
请注意,这是一个重要的限制:如果不是这样,上面的代码可以使用复制构造函数来建立同样良好的转换序列,并且初始化将是不明确的。 (请注意该规则中“A 或 B 和 C”的潜在混淆:它的意思是“(A 或 B)和 C” - 所以我们仅在尝试通过 a 进行转换时受到限制X 的构造函数,其参数类型为 X
)。
我们被委托(delegate)给13.3.1.7
为了收集构造函数,我们可以使用它来进行此转换。让我们从 8.5
开始从一般角度来看待这一段。将我们委托(delegate)给 8.5.4
:
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.
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-qualifiedstd::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).
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. 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
是类类型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.
我们只会考虑 std::vector
的初始化列表作为唯一的候选人,因为我们已经知道其他人不会赢得胜利或不符合论点。它具有以下签名:
vector(initializer_list<std::string>, const Allocator& = Allocator());
现在,将初始值设定项列表转换为 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-defined conversion even in the context of a call to an initializer-list constructor.
现在,初始化列表将成功转换,并且转换顺序是用户定义的转换(从 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). (...)
参见8.5.4/4
最后一步是如何完成的:)
关于c++ - std::initializer_list 作为函数参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2357452/