具有统一初始化的自动将扩展到意外类型

Auto with uniform initialization expands to unexpected type

本文关键字:扩展到 意外 类型 初始化      更新时间:2023-10-16

考虑这个用GCC 4.7.2编译的简短程序g++ -std=c++11 test.cc

#include <memory>
#include <queue>
struct type{
  type(int a) : v(a) {}
  int v;
};
typedef std::shared_ptr<type> type_ptr;
int main(){
  int value = 3;
  std::queue<type_ptr> queue;
  auto ptr{std::make_shared<type>(value)};
  queue.push(ptr);
}

编译器输出以下错误:

src/test.cc: In function ‘int main()’:
src/test.cc:15:17: error: no matching function for call to ‘std::queue<std::shared_ptr<type> >::push(std::initializer_list<std::shared_ptr<type> >&)’
src/test.cc:15:17: note: candidates are:
In file included from /usr/include/c++/4.7/queue:65:0,
                 from src/test.cc:2:
/usr/include/c++/4.7/bits/stl_queue.h:211:7: note: void std::queue<_Tp, _Sequence>::push(const value_type&) [with _Tp = std::shared_ptr<type>; _Sequence = std::deque<std::shared_ptr<type>, std::allocator<std::shared_ptr<type> > >; std::queue<_Tp, _Sequence>::value_type = std::shared_ptr<type>]
/usr/include/c++/4.7/bits/stl_queue.h:211:7: note:   no known conversion for argument 1 from ‘std::initializer_list<std::shared_ptr<type> >’ to ‘const value_type& {aka const std::shared_ptr<type>&}’
/usr/include/c++/4.7/bits/stl_queue.h:216:7: note: void std::queue<_Tp, _Sequence>::push(std::queue<_Tp, _Sequence>::value_type&&) [with _Tp = std::shared_ptr<type>; _Sequence = std::deque<std::shared_ptr<type>, std::allocator<std::shared_ptr<type> > >; std::queue<_Tp, _Sequence>::value_type = std::shared_ptr<type>]
/usr/include/c++/4.7/bits/stl_queue.h:216:7: note:   no known conversion for argument 1 from ‘std::initializer_list<std::shared_ptr<type> >’ to ‘std::queue<std::shared_ptr<type> >::value_type&& {aka std::shared_ptr<type>&&}’
指示 auto 类型

扩展到初始值设定项列表而不是 std::shared_ptr<type>;实际上,将 {...} 替换为 = ... 会使代码在 auto 扩展到正确的类型时编译。

我有点惊讶这个看似明显的用例未能达到预期的结果。特别是当我记得新的括号初始化语法被吹捧为初始化问题的最终解决方案时。

所以我的问题是:这是标准中有意的吗?还是疏忽,甚至是 gcc 错误?还是我只是想错了?

正如Xeo在他的评论中所说,这是标准行为。7.1.6.4 自动说明符 [dcl.spec.auto] 第 6 段指定:

根据 8.3 确定声明 ID 的类型后,使用声明符 ID 的声明变量的类型将使用模板参数推断规则从其初始值设定项的类型确定。设T为变量标识符d确定的类型。通过将auto的出现次数替换为新发明的类型模板参数U或者,如果初始值设定项是大括号初始化列表 (8.5.4(,则替换为 std::initializer_list<U> 来获取T PA然后,使用从函数调用 (14.8.2.1( 中推导的模板参数推导规则确定为变量d推导的类型,其中 P 是函数模板参数类型,d 的初始值设定项是相应的参数。如果 扣除失败,声明格式不正确。

它也被广泛鄙视 - 委员会正在审查一项改变C++14行为的提案。C++14对广义lambda捕获的支持加剧了这个问题。

更新:在厄巴纳(见 N4251 WG21 2014-11 厄巴纳会议纪要中的 CWG 议案 16(中,委员会将 N3922 自动从大括号初始化列表中自动扣除的新规则应用于 C++17 工作文件。他们决定修复允许auto通过添加另一个特殊情况来推断initializer_list的特殊情况。 auto复制列表初始化的工作方式相同,但对于从具有单个元素的大括号初始化列表直接列表初始化auto该元素直接从该元素推断。来自多元素大括号初始化列表的直接列表初始化现在格式不正确。

这意味着给定

auto x = {42};

x的类型为 std::initializer_list<int> ,但在

auto x{42};

x是一个int.