【问题标题】:Auto with uniform initialization expands to unexpected type具有统一初始化的自动扩展为意外类型
【发布时间】:2013-07-09 14:47:51
【问题描述】:

考虑一下这个用 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&lt;type&gt;;事实上,用= ... 替换{...} 会使代码编译为自动扩展为正确的类型。

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

所以我的问题是:这是标准中的意图吗?或者它是一个疏忽甚至是 gcc 错误?还是我只是想错了?

【问题讨论】:

  • 这是标准行为。

标签: c++ c++11 standards auto uniform-initialization


【解决方案1】:

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

一旦根据 8.3 确定了 declarator-id 的类型,使用 declarator-id 声明的变量的类型将根据其类型确定使用模板参数推导规则的初始化程序。让T 成为已为变量标识符d 确定的类型。通过用新发明的类型模板参数U 替换出现的autoT 获取P,或者如果初始化程序是braced-init-list (8.5.4 ),std::initializer_list&lt;U&gt;。为变量 d 推导的类型然后是使用从函数调用 (14.8.2.1) 的模板参数推导规则确定的推导 A,其中 P 是函数模板参数类型和 @987654335 的初始值设定项@ 是相应的参数。如果 扣除失败,声明格式不正确。

它也被广泛鄙视 - 有一个 proposal under review by the committee 来改变 C++14 的行为。 C++14 对广义 lambda 捕获的支持 exacerbates the problem

更新:在 Urbana(参见 N4251 WG21 2014-11 Urbana Minutes 中的 CWG Motion 16),委员会将 N3922 New Rules for auto deduction from braced-init-list 应用于 C++17 工作文件。他们决定通过添加 another 特殊情况来修复允许auto 推断initializer_list 的特殊情况。 auto 对于 copy-list-initialization 的工作方式相同,但对于来自 braced-init-listdirect-list-initialization单个元素auto 直接从该元素推导出来。来自多元素 braced-init-listdirect-list-initialization 现在格式不正确。

这意味着给定

auto x = {42};

x 的类型为 std::initializer_list&lt;int&gt;,但在

auto x{42};

x 是一个int

【讨论】:

  • 谢谢,已接受答案。除非我明确需要统一初始化以避免将来出现问题,否则我将回到普通样式初始化。
  • 此事的最终状态如何?
  • @Etherealone 更新了答案以反映 2014 年 11 月厄巴纳会议上应用的 C++ 工作文件的更改。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多