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

Я понимаю, что для инициализатора в скобках 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

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

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

пустота шаблона g (T);

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

- конечный пример]

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

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


person KnowItAllWannabe    schedule 10.07.2013    source источник
comment
Недавно был задан еще один вопрос SO о том, что вы выяснили. В этом может быть какое-то «почему».   -  person chris    schedule 11.07.2013
comment
Хорошо, нашел. Единственное реальное обсуждение причин - это один комментарий, но вы можете взглянуть на него: stackoverflow.com/questions/17496268/   -  person chris    schedule 11.07.2013
comment
Цитата из Скотта Мейерса: У меня есть Понятия не имею, почему вывод типа для auto и для шаблонов не идентичен. Если знаете, скажите, пожалуйста!.   -  person Andy Prowl    schedule 11.07.2013


Ответы (3)


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

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

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

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> является спорным. Существует предложение для C ++ - после-14 удалить его (и запретить инициализацию с помощью { } или {a, b}, и сделать {a} выводом к типу a).

person Johannes Schaub - litb    schedule 12.07.2013

Причина описана в N2640. :

{} -Список не может выводиться по параметру простого типа T. Например:

template<class T> void count(T); // (1).
struct Dimensions { Dimensions(int, int); };
size_t count(Dimensions); // (2).
size_t n = count({1, 2}); // Calls (2); deduction doesn't
                          // succeed for (1).

Другой пример:

template<class T>
void inc(T, int); // (1)
template<class T>
void inc(std::initializer_list<T>, long); // (2)
inc({1, 2, 3}, 3); // Calls (2). (If deduction had succeeded
                   // for (1), (1) would have been called — a
                   // surprise.)

С другой стороны, возможность вывести initializer_list<X> для T привлекательна тем, что позволяет:

auto x = { 1, 1, 2, 3, 5 };
f(x);
g(x);

что считалось желательным поведением с самого начала обсуждения списков инициализаторов в EWG.

Вместо того, чтобы придумывать хитроумное правило вывода для типа параметра T, соответствующего {} -списку (вариант, который мы использовали в предыдущих набросках и черновиках этого документа), теперь мы предпочитаем обрабатывать это в особом случае для "авто" вывод переменной, когда инициализатор является {} -списком. То есть, для конкретного случая переменной, объявленной со спецификатором типа "auto" и инициализатором {} -list, "auto" выводится как для функции f(initializer_list<T>), а не как для функции f(T).

В заключение, проблема заключается в том, что если мы разрешим {} -списку выводить значение параметра простого типа T, тогда функция с параметром T будет иметь очень высокий приоритет во время разрешения перегрузки, что может вызвать поведение проводной сети (как в приведенных выше примерах) .

person xskxzr    schedule 22.07.2018

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

{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’

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

в auto x = {1, 2, 3}; коде можно определять тип, потому что кодировщик явно сказал: «Неважно, какой это тип», используя auto

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

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

person RiaD    schedule 11.07.2013
comment
Я не понимаю, как выражение auto означает, что тип не важен, а выражение template<typename T> означает другое. В обоих случаях вы рискуете, что выведенный тип может не соответствовать задумке программиста. - person KnowItAllWannabe; 12.07.2013
comment
Я не понимаю второй код. Вы пишете, чтобы показать, что это не список инициализаторов, а в примере {1,2,3} - это список инициализаторов, следовательно, ошибка ... - person 463035818_is_not_a_number; 13.07.2017
comment
То есть я показываю, что в первом коде это не initializer_list. Потому что когда это initialzer_list, он не работает - person RiaD; 13.07.2017