4

考虑这个用 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>; 实际上替换{...}= ...使代码编译为自动扩展为正确的类型。

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

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

4

1 回答 1

12

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

一旦根据 8.3 确定了declarator-id的类型,使用declarator-id声明的变量的类型就使用模板参数推导规则从其初始化程序的类型确定。让T是已为变量标识符确定的类型d。通过将出现的 替换为P新发明的类型模板参数,或者如果初始值设定项是花括号初始化列表(8.5.4),则使用. 然后使用函数调用 (14.8.2.1) 中的模板参数推导规则确定为变量推导的类型,其中TautoUstd::initializer_list<U>dAP是一个函数模板参数类型,初始化器d是对应的参数。如果扣除失败,则声明格式错误。

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

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

这意味着给定

auto x = {42};

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

auto x{42};

x是一个int

于 2013-07-09T15:32:26.343 回答