问题描述
我试图了解组合初始化列表和 const auto
时 C++11 的正确行为应该是什么.对于以下代码,我在 GCC 和 Clang 之间得到了不同的行为,并且想知道哪个是正确的:
I am trying to understand what the correct behavior of C++11 should be when combining initialization lists and const auto
. I am getting different behavior between GCC and Clang for the following code and would like to know which is the correct one:
#include <iostream>
#include <typeinfo>
#include <vector>
int main()
{
const std::initializer_list<int> l1 = { 1, 2, 3 };
const auto l2 = { 1, 2, 3 };
std::cout << "explicit: " << typeid(l1).name() << std::endl;
std::cout << "auto: " << typeid(l2).name() << std::endl;
}
使用 g++ 编译输出为:
Compiled with g++ the output is:
explicit: St16initializer_listIiE
auto: St16initializer_listIKiE
虽然 clang++ 编译版本产生:
While the clang++ compiled version produces:
explicit: St16initializer_listIiE
auto: St16initializer_listIiE
似乎 GCC 正在将 auto
行转换为 std::initializer_list
而 Clang 生成 std::initializer_list
std::vector
时,它会产生问题.因此,以下内容在 Clang 下有效,但会为 GCC 产生编译器错误.
It seems that GCC is turning the auto
line into a std::initializer_list<const int>
while Clang produces std::initializer_list<int>
. The GCC version creates a problem when I use it to initialize a std::vector
. So the following works under Clang but produces a compiler error for GCC.
// Compiles under clang but fails for GCC because l4
std::vector<int> v2 { l2 };
如果 GCC 生成了正确的版本,那么似乎建议扩展各种 STL 容器以包含针对这些情况的另一个列表初始化程序重载.
If GCC is producing the correct version then it seems to suggest that the various STL containers should be extended to include another list initializer overload for these cases.
注意:这种行为在 GCC(4.8、4.9、5.2)和 Clang(3.4 和 3.6)的多个版本中似乎是一致的.
Note: this behavior seems to be consistent across multiple versions of GCC (4.8, 4.9, 5.2) and Clang (3.4 and 3.6).
推荐答案
GCC 错误.[dcl.spec.auto]/p7(引用 N4527):
GCC bug. [dcl.spec.auto]/p7 (quoting N4527):
当使用占位符类型声明的变量被初始化时,[...] 推导出的返回类型或变量类型由其初始值设定项的类型.[...] 否则,让 T
成为声明的类型的变量 [...].如果占位符是 auto
type-specifier,推导的类型是使用模板参数推导的规则确定的.如果初始化是直接列表初始化 [...].[...] 否则,通过将出现的 auto
替换为新发明的类型模板参数 U
或者,如果初始化是复制列表初始化,带有std::initializer_list
.使用模板实参推导规则推导出 U
的值函数调用(14.8.2.1),其中P
是函数模板参数类型和相应的参数是初始值设定项 [...].如果推导失败,声明格式错误.否则,类型对变量或返回类型的推导是通过代入获得的将推导出的U
转化为P
.
When a variable declared using a placeholder type is initialized, [...] the deduced return type or variable type is determined from the type of its initializer. [...] Otherwise, let
T
be the declared type of the variable [...]. If the placeholder is theauto
type-specifier, the deduced type is determined using the rules for template argument deduction. If the initialization is direct-list-initialization [...]. [...] Otherwise, obtainP
fromT
by replacing the occurrences ofauto
with either a new invented type template parameterU
or, if the initialization is copy-list-initialization, withstd::initializer_list<U>
. Deduce a value forU
using the rules of template argument deduction from a function call (14.8.2.1), whereP
is a function template parameter type and the corresponding argument is the initializer [...]. If the deduction fails, the declaration is ill-formed. Otherwise, the type deduced for the variable or return type is obtained by substituting the deducedU
intoP
.
因此,在const auto l2 = { 1, 2, 3 };
中,推导就像函数模板一样进行
Thus, in const auto l2 = { 1, 2, 3 };
, deduction is performed as if for the function template
template<class U> void meow(const std::initializer_list<U>);
给定调用 meow({1, 2, 3})
.
现在考虑无常量的情况 auto l3 = { 1, 2, 3 };
(GCC 正确推断为 std::initializer_list
).这种情况下的推导就像函数模板一样执行
Now consider the const-less case auto l3 = { 1, 2, 3 };
(which GCC correctly deduces as std::initializer_list<int>
). Deduction in this case is performed as if for the function template
template<class U> void purr(std::initializer_list<U>);
给定调用 purr({1, 2, 3})
.
由于忽略了函数参数的顶级 cv 限定,很明显,这两个推导应该产生相同的类型.
Since top-level cv-qualification of function parameters are ignored, it should be obvious that the two deduction should yield the same type.
[temp.deduct.call]/p1:
[temp.deduct.call]/p1:
模板参数推导是通过比较每个函数来完成的模板参数类型(称之为 P
)与调用的相应参数(称为 A
),如下所述.如果 P
是依赖类型,则从P
为某些 P'
[...] 提供 std::initializer_list
[...] 和参数是一个非空的初始化列表(8.5.4),然后推导代替初始化列表的每个元素执行,取P'
作为函数模板参数类型和初始化元素作为它的论据.
Template argument deduction is done by comparing each function template parameter type (call it
P
) with the type of the corresponding argument of the call (call itA
) as described below. IfP
is a dependent type, removing references and cv-qualifiers fromP
givesstd::initializer_list<P'>
[...] for someP'
[...] and the argument is a non-empty initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, takingP'
as a function template parameter type and the initializer element as its argument.
根据1
、2
或3<推导出
,所有 P'
(即U
)int
类型的文字,显然产生 int
.
Deducing P'
(which is U
) against 1
, 2
, or 3
, all literals of type int
, obviously yields int
.
这篇关于Clang 和 GCC 之间的 const auto std::initializer_list 区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!