带括号和初始化列表的自动变量

13

另一个问题中得出:

C++17之后,auto x0{1, 2, 3, 4};, 以前是推导初始化器列表,现在不再允许(当然,我们可以使用auto x0 = {1, 2, 3, 4};代替...)。现在一如既往地避免统一初始化(例如std::vector<int> v({1, 2, 3, 4});),即使用初始化器列表作为参数的显式构造函数调用,并且类比于定义良好的auto x(7);(我也永远不会使用的一种构造形式...),我想出了以下内容:

auto x({1, 2, 3, 4});
// -> std::initializer_list<int> x({1, 2, 3, 4});

这个使用GCC 7.2.0 (mingw64)编译通过了,但是出现了一个警告(而注释版本没有):

非类类型的列表初始化器不应该被括在圆括号中

我在标准中找不到任何相关的内容,所以现在的问题是(仅出于纯粹的兴趣...):

为什么不允许这样做?(这是否被标准涵盖,或者我们需要将其视为GCC的错误?)


1
我很惊讶auto x0 = {1, 2, 3, 4};仍然被允许。 - user7860670
@VTT - 禁止它将破坏像 for(int i : {2, 3, 5, 7}) 这样的无辜结构。 - StoryTeller - Unslander Monica
@VTT chris在引用的问题中提供了命题,导致禁止auto x{1, 2},实际上也禁止了= 变体,但标准委员会在这一点上没有遵循。另一方面,如果它被禁止,我的上述变体将是使用auto创建初始化列表的唯一有效方法,而新标准试图避免这种情况,以支持统一初始化,这可能是允许上述变体的原因... - Aconcagua
1
clang 拒绝了这个。 - Baum mit Augen
@BaummitAugen 好的,关于GCC不会有bug的提示,虽然对问题影响不大:为什么是错误而不是警告? - Aconcagua
显示剩余3条评论
1个回答

6
这段文字涉及到IT技术,翻译如下:

这是一种不规范的写法。简而言之,花括号初始化列表不能在模板参数推导中被推导出来,因为它被视为非推导上下文

6) 参数P的类型A是一个花括号初始化列表,但P不是std::initializer_list或者它的引用:

首先,auto类型推导使用的是函数调用的模板参数推导规则。[dcl.type.auto.deduct]/4

(我强调)

If the placeholder is the auto type-specifier, the deduced type T' replacing T is determined using the rules for template argument deduction. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initialization is copy-list-initialization, with std​::​initializer_­list<U>. Deduce a value for U using the rules of template argument deduction from a function call, where P is a function template parameter type and the corresponding argument is e. If the deduction fails, the declaration is ill-formed. [ Example:

const auto &i = expr;

The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:

template <class U> void f(const U& u);

— end example ]

请注意,auto x({1, 2, 3, 4}); 是直接初始化,而不是复制初始化,那么发明的类型模板参数只是U,而不是std::​initializer_­list<U>,对应的参数是{1, 2, 3, 4}
从函数调用中推断模板参数中,不能从花括号初始化列表中推断出模板参数。 [temp.deduct.call]/1

Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std​::​initializer_­list or P'[N] for some P' and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context ([temp.deduct.type]). [ Example:

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

— end example ]


现在至少有一件事是确定的:无论GCC是否正确发出警告(或clang拒绝),GCC的诊断消息指向的方向是错误的... - Aconcagua
@Aconcagua 是的,我同意它很难理解想要表达什么。标准只要求编译器对格式不正确的代码发出诊断,而不需要指定如何;因此从理论上讲,在这种情况下gcc和clang都是正确的。 - songyuanyao
2
@Aconcagua template <typename T> void f(T t); f({1, 2, 3}); 总是不合法的。在复制列表初始化中,将 {...} 推导为 std::initializer_list<> 是一条特殊规则,仅适用于 auto - songyuanyao
1
@Aconcagua 像往常一样,我想指出一个实现可以自由添加任意数量的_语言扩展_。引用(intro.compliance/8)中的话:符合规范的实现可以有扩展(包括额外的库函数),只要它们不改变任何格式良好程序的行为。实现需要诊断使用此类扩展的程序是否根据此国际标准格式不良。然而,一旦这样做,它们就可以编译和执行这些程序。 - Arne Vogel
1
@ArneVogel 嗯,实际上已经发出了一个警告(请参见问题),因此在这方面根本没有错误。 - Aconcagua
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接