Почему для упрощенных инициализаторов отличаются вычеты типа "авто" и "шаблон"?

Я понимаю, что, учитывая строгий инициализатор, auto выведет тип std::initializer_list, в то время как вывод типа шаблона не будет выполнен:

auto var = { 1, 2, 3 };   // type deduced as std::initializer_list<int>

template<class T> void f(T parameter);

f({ 1, 2, 3 });          // doesn't compile; type deduction fails

Я даже знаю, где это указано в стандарте С++ 11:14.8.2.5/5 bullet 5:

[Это не выводимый контекст, если программа имеет] Параметр функции, для которого связанный аргумент является списком инициализаторов (8.5.4), но параметр не имеет std:: initializer_list или ссылки на возможно cv-qualified std:: initializer_list тип. [Пример:

шаблон void g (T);

г ({1,2,3});//ошибка: не выводится аргумент для T

-end пример]

То, что я не знаю или не понимаю, почему это отличие в поведении типа дедукции существует. Спецификация на компакт-диске С++ 14 такая же, как в С++ 11, поэтому, по-видимому, комитет по стандартизации не рассматривает поведение С++ 11 как дефект.

Кто-нибудь знает, почему auto выводит тип для упрощенного инициализатора, но шаблонам не разрешено? Хотя спекулятивные объяснения формы "это может быть причиной" интересны, меня особенно интересуют объяснения от людей, которые знают, почему стандарт был написан так, как он был.

Ответ 1

Есть две важные причины, по которым шаблоны не делают никакого вывода (два, которые я помню в обсуждении с ответственным парнем)

  • Обеспокоена будущими языковыми расширениями (есть несколько значений, которые вы могли бы придумать), а как насчет того, хотим ли мы представить идеальную переадресацию для аргументов с аргументами аргументов с расширенными функциями?)

  • Скобки иногда могут корректно инициализировать параметр функции, зависящий от

template<typename T>
void assign(T &d, const T& s);
int main() {
  vector<int> v;
  assign(v, { 1, 2, 3 });
}

Если T будет выводиться с правой стороны на initializer_list<int>, но с левой стороны на vector<int>, это не сработает из-за противоречивого вывода аргумента.

Вывод для auto - initializer_list<T> является спорным. Существует предложение для С++ - после-14, чтобы удалить его (и запретить инициализацию с помощью { } или {a, b}, и сделать {a} выводящим тип a).

Ответ 2

Прежде всего, это "умозрительные объяснения формы", это может быть причиной ", как вы ее называете.

{1,2,3} не только std::initializer_list<int>, но также позволяет инициализировать типы без конструктора. Например:

#include <initializer_list>

struct x{
    int a,b,c;
};

void f(x){

}
int main() {
    f({1,2,3});
}

- правильный код. Чтобы показать, что это не initializer_list, можно увидеть следующий код:

#include <initializer_list>

struct x{int a,b,c;};

void f(x){

}
int main() {
    auto il = {1, 2, 3};
    f(il);
}

Ошибка:

prog.cpp: In function ‘int main()’:
prog.cpp:10:9: error: could not convert ‘il’ from ‘std::initializer_list<int>’ to ‘x’

И теперь на вопрос "В чем разница?"

in auto x = {1, 2, 3}; введите код ОК, чтобы определить тип, потому что кодер явно сказал: "Это не важно, какой тип", используя auto

В случае шаблона функции он может быть уверен, что использует другой тип. И хорошо предотвращать ошибки в двусмысленных случаях (это не похоже на стиль С++, через).

Особенно плохо это будет в случае, если была 1 функция f(x), а затем она была изменена на шаблонную. Программист написал, чтобы использовать его как x, и после добавления новой функции для другого типа она слегка изменилась, чтобы вызвать совершенно другую.