const auto std :: initializer_list Clang和GCC之间的区别 [英] const auto std::initializer_list difference between Clang and GCC

查看:281
本文介绍了const auto std :: initializer_list Clang和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-qualifiers std :: initializer_list< P'> [...] for some P' [...]
参数是一个非空的初始化器列表(8.5.4),然后对初始化器列表的每个元素执行扣除
,取
P'作为函数模板参数类型和初始值设置元素
作为其参数。


Deducing <$对 1 ,<$ c> P' 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 the auto type-specifier, the deduced type is determined using the rules for template argument deduction. If the initialization is direct-list-initialization [...]. [...] Otherwise, 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 (14.8.2.1), where P 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 deduced U into P.

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 it A) as described below. If P is a dependent type, removing references and cv-qualifiers from P gives std::initializer_list<P'> [...] for some P' [...] and the argument is a non-empty initializer list (8.5.4), 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.

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屋!

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