[英]ctor is ambiguous between single and multiple std::initializer_list ctors on clang and gcc but not msvc
[英]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
行转换为std::initializer_list<const int>
而 Clang 生成std::initializer_list<int>
。 当我使用 GCC 版本初始化std::vector
时,它会产生问题。 因此,以下内容在 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)的多个版本中似乎是一致的。
海湾合作委员会错误。 [dcl.spec.auto]/p7(引用 N4527):
当初始化使用占位符类型声明的变量时,[...] 推导出的返回类型或变量类型由其初始化程序的类型确定。 [...] 否则,让
T
成为变量 [...] 的声明类型。 如果占位符是auto
类型说明符,则使用模板参数推导规则确定推导类型。 如果初始化是直接列表初始化[...]。 [...] 否则,通过用新发明的类型模板参数U
替换auto
的出现,或者,如果初始化是copy-list-initialization ,用std::initializer_list<U>
替换从T
获得P
使用函数调用中的模板参数推导规则 (14.8.2.1) 推导U
的值,其中P
是函数模板参数类型,相应的参数是初始化器 [...]。 如果推导失败,则声明格式错误。 否则,通过将推导的U
入P
获得为变量或返回类型推导的类型。
因此,在const auto l2 = { 1, 2, 3 };
, 推导如同函数模板一样执行
template<class U> void meow(const std::initializer_list<U>);
给定电话meow({1, 2, 3})
。
现在考虑无常量的情况auto l3 = { 1, 2, 3 };
(GCC 正确推断为std::initializer_list<int>
)。 在这种情况下的推导如同函数模板一样执行
template<class U> void purr(std::initializer_list<U>);
给定电话purr({1, 2, 3})
。
由于忽略了函数参数的顶级 cv 限定,很明显,这两个推导应该产生相同的类型。
[temp.deduct.call]/p1:
模板参数推导是通过将每个函数模板参数类型(称为
P
)与调用的相应参数类型(称为A
)进行比较来完成的,如下所述。 如果P
是依赖类型,则从P
删除引用和 cv 限定符会为某些P'
[...] 提供std::initializer_list<P'>
[...] 并且参数是非空初始化列表(8.5 .4),然后对初始化列表的每个元素执行推导,将P'
作为函数模板参数类型,并将初始化元素作为其参数。
针对1
、 2
或3
推断P'
(即U
),所有int
类型的文字,显然会产生int
。
有一个 gcc 错误报告从支撑初始化列表中错误的自动推论关于这个和类似的情况,理查德史密斯指出这是一个 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 为变量的声明类型或函数的返回类型。 如果占位符是自动类型说明符,则使用模板参数推导规则确定推导类型。 [...] 否则,通过用新发明的类型模板参数 U 替换 auto 的出现,或者如果初始值设定项是花括号初始化列表,用 std::initializer_- 列表替换出现的 auto 来从 T 获得 P。 使用函数调用中的模板参数推导规则 (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
— 结束示例 ] [ 示例:
const auto &i = expr;
i 的类型是以下发明的函数模板的调用 f(expr) 中参数 u 的推导类型:
template <class U> void f(const U& u);
—结束示例]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.