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

时间:2013-07-09 14:47:52

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

考虑使用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>&&}’

表示自动类型已扩展为初始值设定项列表而非std::shared_ptr<type>;实际上用{...}替换= ...会使代码编译为自动扩展为正确的类型。

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

所以我的问题是:这是否符合标准?或者它是一个疏忽甚至是gcc的错误?或者我只是想错了?

1 个答案:

答案 0 :(得分:10)

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

  

根据8.3确定 declarator-id 的类型后,使用 declarator-id 的声明变量的类型由其类型确定初始化程序使用模板参数推导的规则。设T为已为变量标识符d确定的类型。通过使用新发明的类型模板参数P替换T的出现次数,或者如果初始化程序是 braced-init-list,则从auto获取U (8.5.4),std::initializer_list<U>。根据函数调用(14.8.2.1)使用模板参数推导的规则确定推导出的变量d的类型A,其中P是函数模板参数类型并且d的初始值设定项是相应的参数。如果   扣除失败,声明形成不良。

它也被广泛鄙视 - 有一个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工作文件。他们决定修改特殊情况,允许auto通过添加另一个特殊情况来推断initializer_list。对于 copy-list-initialization auto的工作方式相同,但对于来自 braced-init-list的 direct-list-initialization >使用单个元素auto直接从该元素推导出。来自多元素 braced-init-list 直接列表初始化现在格式不正确。

这意味着给定

auto x = {42};

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

auto x{42};

xint