Авто с равномерной инициализацией расширяется до неожиданного типа
Рассмотрим эту короткую программу, скомпилированную с 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
,