Рассмотрим эту короткую программу, скомпилированную с 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? Или я просто думаю об этом неправильно?
Как говорит Ксео в своем комментарии, это стандартное поведение. 7.1.6.4 авто спецификатор [dcl.spec.auto] пункт 6 определяет:
После того, как тип описатель-идентификатор был определен в соответствии с 8.3, тип объявленной переменной с использованием описатель-идентификатор определяется по типу его инициализатора по правилам вывода аргументов шаблона. Позволять
T
быть типом, который был определен для идентификатора переменнойd
, получатьP
отT
заменив вхожденияauto
либо с новым параметром шаблона изобретенного типаU
или, если инициализатор является приготовился-INIT-лист (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 Урбана Минут) комитет подал заявку N3922 Новые правила автоматического удержания из списка braced-init-list на рабочий документ C ++ 17. Они решили исправить особый случай, который позволяет auto
вывести initializer_list
добавляя другой особый случай. auto
работает так же для копирование списка инициализация, но для прямой список инициализация из приготовился-INIT-лист с одним элементом auto
выводит из этого элемента напрямую. прямой список инициализация из многоэлементного приготовился-INIT-лист сейчас плохо сформирован.
Это означает, что с учетом
auto x = {42};
x
имеет тип std::initializer_list<int>
, но в
auto x{42};
x
является int
,
Других решений пока нет …