const auto std :: initializer_list Clang和GCC之间的区别 [英] const auto std::initializer_list difference between Clang and GCC
问题描述
我想了解当结合初始化列表和 const auto
时,C ++ 11应该是正确的行为。对于下面的代码,我在GCC和Clang之间得到不同的行为,并想知道哪个是正确的:
#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 ++编译输出为:
explicit:St16initializer_listIiE
auto:St16initializer_listIKiE
虽然clang ++编译版本产生:
explicit:St16initializer_listIiE
auto:St16initializer_listIiE
似乎GCC正在将 auto
c $ c> std :: initializer_list< const int> ,而Clang生成 std :: initializer_list< int>
。 GCC版本创建一个问题,当我使用它来初始化 std :: vector
。所以下面的工作在Clang下,但为GCC产生一个编译器错误。
//在clang下编译,但对于GCC失败,因为l4
std :: vector< int& v2 {l2};
如果GCC生成正确的版本,那么似乎建议将各种STL容器扩展到对这些情况包括另一个列表初始化器重载。
注意:这种行为似乎在多个版本的GCC(4.8,4.9,5.2)和Clang(3.4和3.6)之间是一致的。
GCC错误。 [dcl.spec.auto] / p7(引用N4527):
当使用占位符类型声明的变量初始化时,
[...]推导的返回类型或变量类型由其初始化程序的
类型确定。 [...]否则,让T
是变量[...]的声明类型
。如果占位符是auto
类型说明符,则使用模板参数扣除规则确定推导的类型。如果初始化是
direct-list-initialization [...]。 [...]否则,通过替换出现的auto
T
获得P
/ code>与一个新的发明的
类型模板参数U
或者,如果初始化是
copy-list-initialization 与std :: initializer_list< U>
。使用从
函数调用(14.8.2.1)中扣除模板参数的规则,推导U
的值,其中P
是一个函数模板参数
类型,相应的参数是initializer [...]。如果
扣除失败,声明是不成立的。否则,通过将推导出的U
替换为P $来获得变量或返回类型推导的类型
const auto l2 = {1,2,3};
/ code>,如同对于功能模板
模板< class U> void meow(const std :: initializer_list< U>);
给予呼叫 meow({1,2,3} code>。
现在考虑一下const auto l3 = {1,2,3};
(其中GCC正确地推导为 std :: initializer_list< int>
)。在这种情况下的扣除被执行,如同对于功能模板
template< class U> void purr(std :: initializer_list< U>);
给予调用 purr({1,2,3} code>。
由于函数参数的顶级cv限定被忽略,所以显然应该产生相同的类型。 p>
[temp.deduct.call] / p1:
模板参数扣除是通过比较每个函数
模板参数类型(调用P
)与
对应的类型调用的参数(调用A
),如下所述。
如果P
是一个依赖类型,从
P
中删除引用和cv-qualifiersstd :: initializer_list< P'>
[...] for someP'
[...]
参数是一个非空的初始化器列表(8.5.4),然后对初始化器列表的每个元素执行扣除
,取
P'
作为函数模板参数类型和初始值设置元素
作为其参数。
Deducing <$对 1
,<$ c> U
$ c> 2 或 3
,所有类型 int
的文本, code> int 。
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;
}
Compiled with g++ the output is:
explicit: St16initializer_listIiE
auto: St16initializer_listIKiE
While the clang++ compiled version produces:
explicit: St16initializer_listIiE
auto: St16initializer_listIiE
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 };
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.
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 bug. [dcl.spec.auto]/p7 (quoting N4527):
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
.
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>);
given the call meow({1, 2, 3})
.
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>);
given the call purr({1, 2, 3})
.
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:
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.
Deducing P'
(which is U
) against 1
, 2
, or 3
, all literals of type int
, obviously yields int
.
这篇关于const auto std :: initializer_list Clang和GCC之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!