std :: initializer_list,带有括号的初始化和标头 [英] std::initializer_list, braced initialization and header

查看:189
本文介绍了std :: initializer_list,带有括号的初始化和标头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在阅读另一个主题时,我遇到了一种奇怪的行为,至少对我而言. 这整个思想源于auto与花括号之间的特殊交互.如果您编写类似这样的内容:

auto A = { 1, 2, 3 }

编译器将推断Astd::initializer_list.奇怪的是,类似的规则不仅适用于auto(可能有特殊原因),还适用于其他事物. 如果您编写以下内容:

template<typename T>
void f(std::vector<T> Vector)
{
    // do something
}

您当然不能这样称呼它:

f({ 1, 2, 3});

即使可以初始化std::vector.但是,如果将std::vector替换为std::initializer_list,该调用将起作用,并且编译器将正确推断int作为类型T.但是,更有趣的是,在前一种情况下,您需要#include <vector>,在后一种情况中,您不需要#include <initializer_list>.这让我开始思考,经过测试,我意识到std::initializer_list不需要自己的标头,因此在某种程度上是基本"功能的一部分.

此外,为了使所有事情都有意义,std::initializer_list应该或多或少地与标准对象一样,即lambda与可调用对象(严格意义上就是带有operator()的对象)相同.换句话说,未命名的括号定义应默认为std::initializer_list,就像lambda是(大部分)未命名的可调用对象一样.

这个推理正确吗?而且,这种行为是否可以改变?如果可以,如何改变?

更新:发现initializer_list的标头是从iostream传递而来的(确实很奇怪).但是,问题仍然存在:为什么该调用适用于std::initializer_list而不适用​​于std::vector?

解决方案

它格式不正确(因此需要进行诊断std::initializer_list,则不包含initializer_list标头.我们可以从 [dcl.init.list] p2 中看到这一点. :

...模板std :: initializer_list尚未预定义;如果标题< initializer_list>是 在使用std :: initializer_list之前不包括在内-甚至是隐式使用,其中类型不是 命名为(9.1.7.4)-程序格式错误.

您很有可能会传递性地包含标头,该标头格式正确,但会使您的代码更脆弱,因此请包含您使用的标头.

我们可以从一个实时的godbolt示例中看到,不包含任何附件,我们可以从 gcc/clang/获得诊断信息MSVC 例如:

error: use of undeclared identifier 'std'    
void foo( std::initializer_list<int>) {
          ^

(包括<vector><iostream>),我们不再获得诊断.

[temp. deduct.type] p5 告诉我们这是一个非推论上下文:

非推论上下文是:
...
-函数参数,其关联参数为初始值设定项列表([dcl.init.list]),但该参数不具有为其指定从初始值设定项列表进行演绎的类型([temp.deduct.call]). [示例:

template<class T> void g(T);
g({1,2,3});                 // error: no argument deduced for T

-示例example]
...

另请参见 [temp.deduct.call] p1 :

...否则,初始化程序列表参数会导致该参数被视为非推导上下文([temp.deduct.type])...

While reading about a different topic I came across a weird behaviour, at least to me. This whole thought originated from the special interactions between auto and braces. If you write something like:

auto A = { 1, 2, 3 }

the compiler will deduce A to be a std::initializer_list. The weird thing is that a similar rule applies not only to auto, where there can be special reasons for it, but also to other things. If you write the following:

template<typename T>
void f(std::vector<T> Vector)
{
    // do something
}

you can't of course call it in this way:

f({ 1, 2, 3});

even though a std::vector can be braced initialized. However, if you substitute the std::vector with std::initializer_list, the call works and the compiler will properly deduce int as the type T. The more interesting thing is, however, that in the former case you need to #include <vector>, in the latter you don't need to #include <initializer_list>. This made me think and after a test I realized somehow std::initializer_list don't need its own header, so it is in some way part of the "base" features.

Moreover, for everything to make sense, std::initializer_list should be to standard objects in more or less the same way lambdas are to callable objects (in the strictest meaning, that is an object with a operator()). In other words, unnamed braced definitions should default to std::initializer_list just like lambdas are (mostly) unnamed callable objects.

Is this reasoning correct? Moreover, can this behaviour be changed and, if so, how?

UPDATE: the header for initializer_list was found to be included transitively from iostream (really weird). However, the question remains: why the call works for std::initializer_list and not for std::vector?

解决方案

It is ill-formed (so it requires a diagnostic) to not include the initializer_list header if we use std::initializer_list. We can see this from [dcl.init.list]p2:

... The template std::initializer_list is not predefined; if the header <initializer_list> is not included prior to a use of std::initializer_list — even an implicit use in which the type is not named (9.1.7.4) — the program is ill-formed.

Mostly likely you are including the header transitively, which is well-formed but makes your code more fragile, so include what you use.

We can see from a live godbolt example that having no includes we obtain a diagnostic as required from gcc/clang/MSVC e.g.:

error: use of undeclared identifier 'std'    
void foo( std::initializer_list<int>) {
          ^

and including either <vector> or <iostream> we no longer obtain a diagnostic.

Why it does not deduce as you expect is covered by [temp.deduct.type]p5 which tells us this is a non-deduced context:

The non-deduced contexts are:
...
- A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).> [ Example:

template<class T> void g(T);
g({1,2,3});                 // error: no argument deduced for T

— end example  ]
...

also see [temp.deduct.call]p1:

... Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context ([temp.deduct.type]) ...

这篇关于std :: initializer_list,带有括号的初始化和标头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆