const auto std :: initializer_list Clang和GCC之间的区别

时间:2015-09-11 03:54:37

标签: c++ c++11 gcc stl clang

我试图理解在组合初始化列表和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行转换为std::initializer_list<const int>,而Clang会生成std::initializer_list<int>。当我使用它来初始化std::vector时,GCC版本会产生问题。因此,以下在Clang下工作,但会为GCC产生编译器错误。

// Compiles under clang but fails for GCC because l4
std::vector<int> v2 { l2 };

如果GCC正在生成正确的版本,那么它似乎建议应该扩展各种STL容器以包含这些情况的另一个列表初始化程序重载。

注意:这种行为在GCC(4.8,4.9,5.2)和Clang(3.4和3.6)的多个版本中似乎是一致的。

2 个答案:

答案 0 :(得分:10)

GCC错误。 [dcl.spec.auto] / p7(引用N4527):

  

初始化使用占位符类型声明的变量时   [...]推导出的返回类型或变量类型由   其初始化程序的类型。 [...]否则,让T成为声明的类型   变量[...]。如果占位符是auto    type-specifier ,推导出的类型是使用模板参数推导的规则确定的。如果初始化是   直接列表初始化 [...]。 [...]否则,通过将P的出现替换为新发明的T,从auto获取U   类型模板参数std::initializer_list<U>或者,如果初始化是    copy-list-initialization U。使用a中的模板参数推导规则推导P的值   函数调用(14.8.2.1),其中U是函数模板参数   type和相应的参数是初始化器[...]。如果   扣除失败,声明形成不良。否则,类型   通过替换获得的变量或返回类型的推导   推导出Pconst auto l2 = { 1, 2, 3 };

因此,在template<class U> void meow(const std::initializer_list<U>); 中,对函数模板执行推导

meow({1, 2, 3})

给出电话auto l3 = { 1, 2, 3 };

现在考虑无常数std::initializer_list<int>(GCC正确推导为template<class U> void purr(std::initializer_list<U>); )。这种情况下的扣除就像功能模板

一样
purr({1, 2, 3})

给出电话P

由于忽略了函数参数的顶级cv限定,显然两个推论应该产生相同的类型。

[temp.deduct.call] / P1:

  

通过比较每个函数来完成模板参数推导   模板参数类型(称之为A)的类型   相应的调用参数(称之为P)如下所述。   如果P是依赖类型,则从中删除引用和cv限定符   std::initializer_list<P'>为某些P' [...]提供了P' [...]   参数是一个非空的初始化列表(8.5.4),然后是演绎   取而代之的是执行初始化列表的每个元素   P'作为函数模板参数类型和初始化元素   作为其论点。

U12推导3int),int类型的所有文字,显然都会产生list

答案 1 :(得分:6)

有一个关于此案例和类似案例的gcc错误报告wrong auto deduction from braced-init-list,理查德史密斯表示这是一个gcc错误:

  

更简单的测试用例:

#include <initializer_list>
const auto r = { 1, 2, 3 };
using X = decltype(r);
using X = const std::initializer_list<int>;
     

失败,因为decltype(r)被推断为const std::initializer_list<const int>而不是const std::initializer_list<int>

C ++标准草案的部分是7.1.6.4 [dcl.spec.auto] 部分,其中说:

  

初始化使用占位符类型声明的变量,或者在函数中发生return语句   声明的返回类型包含占位符类型,推导的返回类型或变量类型   根据其初始化程序的类型确定。 [...]设T是函数的变量或返回类型的声明类型。如果   占位符是自动类型说明符,推导的类型是使用模板参数的规则确定的   扣除。 [...]否则,通过用a替换auto的出现来从T获得P.   新发明的类型模板参数U或者,如果初始化程序是braced-init-list,则使用std :: initializer_-   名单。使用函数调用中的模板参数推导规则(14.8.2.1)为U推导一个值,   其中P是函数模板参数类型,初始值设定项是相应的参数[...] [例如:

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
     

-end example] [例如:

const auto &i = expr;
     

i的类型是以下发明的函数模板的调用f(expr)中参数u的推导类型:

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

-end example]