Авто с равномерной инициализацией расширяется до неожиданного типа

Рассмотрим эту короткую программу, скомпилированную с 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 ответ

Решение

Как говорит Ксео в своем комментарии, это стандартное поведение. 7.1.6.4 автоопределитель [dcl.spec.auto], пункт 6, определяет:

Как только тип идентификатора объявления определен в соответствии с 8.3, тип объявленной переменной с использованием идентификатора объявления определяется по типу ее инициализатора с использованием правил для вывода аргументов шаблона. Позволять T быть типом, который был определен для идентификатора переменной d, получать P от T заменив вхождения auto либо с новым параметром шаблона изобретенного типа U или, если инициализатор является списком фигурных скобок (8.5.4), с std::initializer_list<U>, Тип, выведенный для переменной d затем выводится A определяется с использованием правил вывода аргументов шаблона из вызова функции (14.8.2.1), где P тип параметра шаблона функции и инициализатор для d это соответствующий аргумент. Если вычет не удался, декларация неверна.

Это также широко презирается - комитет рассматривает предложение об изменении поведения для C++14. Поддержка C++ 14 обобщенного лямбда-захвата усугубляет проблему.

Обновление: в Урбане (см. CWG Motion 16 в N4251, WG21 2014-11, протокол Urbana) комитет применил новые правила N3922 для автоматического вывода из фигурного списка инициализации в рабочий документ C++17. Они решили исправить особый случай, который позволяет auto вывести initializer_list добавив еще один особый случай. auto работает так же для инициализации копирования списка, но для прямой инициализации списка из списка фигурных скобок с одним элементом auto выводит из этого элемента напрямую. прямая инициализация списка из многоэлементного списка фигурных скобок теперь плохо сформирована.

Это означает, что с учетом

auto x = {42};

x имеет тип std::initializer_list<int>, но в

auto x{42};

x является int,

Другие вопросы по тегам