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

dav*_*aja 13 c++ gcc stl clang c++11

我试图理解组合初始化列表和C++ 11时应该是什么样的正确行为const auto.我在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;
}
Run Code Online (Sandbox Code Playgroud)

用g ++编译输出是:

explicit: St16initializer_listIiE
auto:     St16initializer_listIKiE
Run Code Online (Sandbox Code Playgroud)

而clang ++编译版本产生:

explicit: St16initializer_listIiE
auto:     St16initializer_listIiE
Run Code Online (Sandbox Code Playgroud)

似乎GCC正在将这auto条线转变为std::initializer_list<const int>Clang生产的一段时间std::initializer_list<int>.当我使用它来初始化a时,GCC版本会产生问题std::vector.因此,以下在Clang下工作,但会为GCC产生编译器错误.

// Compiles under clang but fails for GCC because l4
std::vector<int> v2 { l2 };
Run Code Online (Sandbox Code Playgroud)

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

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

T.C*_*.C. 10

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

当初始化使用占位符类型声明的变量时,推导的返回类型或变量类型由其初始化程序的类型确定.[...]否则,让我们T声明变量[...]的类型.如果占位符是auto 类型说明符,则使用模板参数推导的规则确定推导的类型.如果初始化是 直接列表初始化 [...].[...]否则,通过用新发明的类型模板参数替换出现来获得P,或者如果初始化是 copy-list-initialization,则用.推断一个值TautoUstd::initializer_list<U>U使用函数调用中的模板参数推导规则(14.8.2.1),其中P是函数模板参数类型,相应的参数是初始化器[...].如果扣除失败,则声明格式不正确.否则,推断出该变量或返回类型的类型是由代推导得到UP.

因此,在中const auto l2 = { 1, 2, 3 };,对功能模板执行推导

template<class U> void meow(const std::initializer_list<U>);
Run Code Online (Sandbox Code Playgroud)

接到电话meow({1, 2, 3}).

现在考虑无常量情况auto l3 = { 1, 2, 3 };(GCC正确推断为std::initializer_list<int>).在这种情况下的演绎就像执行功能模板一样

template<class U> void purr(std::initializer_list<U>);
Run Code Online (Sandbox Code Playgroud)

接到电话purr({1, 2, 3}).

由于忽略了函数参数的顶级cv限定,因此很明显两个推导应该产生相同的类型.


[temp.deduct.call]/P1:

模板参数推导是通过将每个函数模板参数类型(调用它P)与调用的相应参数的类型(调用它)进行比较来完成的,A如下所述.如果P是一个依赖型,从除去引用和cv修饰符 Pstd::initializer_list<P'>[...]对于一些P'[...]和参数是一个非空的初始化列表(8.5.4),然后进行扣除,而不是为每个元素初始化列表, P'作为函数模板参数类型和初始化元素作为其参数.

推导P'(它是U)对1,23,类型的所有文字int,明显产生int.

  • 我没有看到*top-level cv-qualification*在这里有什么问题,问题是`initializer_list <const int>`而不是`const initializer_list <int>` (2认同)

Sha*_*our 6

有一个gcc错误报告错误自动从braced-init-list中扣除了关于这个和类似的案例,Richard Smith表示它是一个gcc bug:

更简单的测试用例:

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

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

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

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

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

- 示例] [示例:

const auto &i = expr;
Run Code Online (Sandbox Code Playgroud)

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

template <class U> void f(const U& u);
Run Code Online (Sandbox Code Playgroud)

- 末端的例子]