c++ - std::initializer_list 作为函数参数

标签 c++ c++11 initializer-list

出于某种原因,我认为 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.

非聚合类Xstd::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-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).

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/

相关文章:

c++ - 如果不是 intializer_list,什么是花括号封闭列表?

c++ - 类成员 STL 容器 (const std::array) 的编译时创建,其中填充了元素

c++ - 接受单个文件或单个目录的 QFileDialog

c++ - 当boost线程被回收时,在其中创建的临时变量会从内存中删除吗?

c++ - 如何使用 CLion 在 MAC OS X 上发出 EOF 信号

c++ - 在将 unique_ptr 移入我的基类构造函数之前,如何从 unique_ptr 中提取原始指针

c++ - 作为 std::initializer_list 对象的抽象类

c++ - 检查内存泄漏

c++ - 编译一个相当简单的c++ 11程序时gcc和clang之间的不同结果

依赖于编译器特定代理的 C++11 库功能