Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 20

Скотт Мейерс

template<typename Т>    // Объявление шаблона с параметром

void f(Т param);        // эквивалентно объявлению x

f({ 11, 23, 9 });       // Ошибка вывода типа для Т

Однако, если вы укажете в шаблоне, что param представляет собой std::initializer_list<T> для некоторого неизвестного Т, вывод типа шаблона сможет определить, чем является Т:

template<typename Т>

void f(std::initializer_list<T> initList);

f({ 11, 23, 9 }); // Вывод int в качестве типа Т, а тип

                  // initList - std::initializer_list<int>

Таким образом, единственное реальное различие между выводом типа auto и выводом типа шаблона заключается в том, что auto предполагает, что инициализатор в фигурных скобках представляет собой std::initializer_list, в то время как вывод типа шаблона этого не делает.

Вы можете удивиться, почему вывод типа auto имеет специальное правило для инициализаторов в фигурных скобках, в то время как вывод типа шаблона такого правила не имеет. Но я и сам удивлен. Увы, я не в состоянии найти убедительное объяснение. Но “закон есть закон”, и это означает, что вы должны помнить, что если вы объявляете переменную с использованием ключевого слова auto и инициализируете ее с помощью инициализатора в фигурных скобках, то выводимым типом всегда будет std::initializer_list. Особенно важно иметь это в виду, если вы приверженец философии унифицированной инициализации — заключения инициализирующих значений в фигурные скобки как само собой разумеющегося стиля. Классической ошибкой в С++11 является случайное объявление переменной std::initializer_list там, где вы намеревались объявить нечто иное. Эта ловушка является одной из причин, по которым некоторые разработчики используют фигурные скобки в инициализаторах только тогда, когда обязаны это делать. (Когда именно вы обязаны так поступать, мы рассмотрим в разделе 3.1.)

Что касается С++ 11, то на этом история заканчивается, но для С++14 это еще не конец. С++14 допускает применение auto для указания того, что возвращаемый тип функции должен быть выведен (см. раздел 1.3), а кроме того, лямбда-выражения С++ 14 могут использовать auto в объявлениях параметров. Однако такое применение auto использует вывод типа шаблона, а не вывод типа auto. Таким образом, функция с возвращаемым типом auto, которая возвращает инициализатор в фигурных скобках, компилироваться не будет:

auto createInitList() {

 return { 1, 2, 3 }; // Ошибка: невозможно вывести

}                    // тип для { 1, 2, 3 }

То же самое справедливо и тогда, когда auto используется в спецификации типа параметра в лямбда-выражении С++14:

std::vector<int> v;

auto resetV =

 [&v](const auto& newValue) { v = newValue; }; // C++14

resetV({ 1, 2, 3 }); // Ошибка: невозможно вывести

                     // тип для { 1, 2, 3 }

Следует запомнить

• Вывод типа auto обычно такой же, как и вывод типа шаблона, но вывод типа auto, в отличие от вывода типа шаблона, предполагает, что инициализатор в фигурных скобках представляет std::initializer_list.

• auto в возвращаемом типе функции или параметре лямбда-выражения влечет применение вывода типа шаблона, а не вывода типа auto.