#c #c 11 #vector #overloading #initializer-list
#c #c 11 #вектор #перегрузка #инициализатор-список
Вопрос:
Рассмотрим приведенный ниже код:
#include <iostream>
#include <vector>
void f(std::vector<int> v) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
void f(int n) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
int main()
{
f({42}); // the int overload is being picked up
}
Я был немного удивлен, осознав, что в этом случае происходит перегрузка int, т. Е. Вывод программы является:
void f(int)
с предупреждением
предупреждение: фигурные скобки вокруг скалярного инициализатора [-Wbraced-scalar-init] f({42});
Конечно, это происходит только тогда, когда я передаю список из 1 элемента в качестве аргумента, в противном случае происходит std::vector
перегрузка.
Почему {42}
обрабатывается как скаляр, а не как init-list? Есть ли какой-либо способ заставить компилятор выбирать std::vector
перегрузку (без явного построения std::vector<int>{42}
) даже в списках с одним элементом?
PS: std::vector
имеет конструктор init-list
vector(std::initializer_list<T> init, const Allocatoramp; alloc = Allocator());
смотрите (7) из cppreference .
Комментарии:
1. Вы могли бы добавить перегрузку
void f(std::initializer_list)
.2. @Jarod42 Но у
vector
естьinitializer_list
ctor, почему он не распознается? AFAIK, ctors списка инициализации превосходит все остальное. Я думаю, что есть правило, в котором говорится, что{42}
является int, а не init-list (конечно, выполняется для чисел, отличных от 42 :))3. Я бы предложил спроектировать класс так, чтобы аргумент single-int вел себя так же, как вектор одного int.
Ответ №1:
Связанный инициализатор не имеет типа, мы не можем сказать, что {42}
это int
or std::initializer_list<int>
. Когда он используется в качестве аргумента, для вызова перегруженной функции будут применяться специальные правила для разрешения перегрузки.
(выделено мной)
- В противном случае, если тип параметра не является классом, а список инициализаторов содержит один элемент, для преобразования элемента в тип параметра требуется неявная последовательность преобразования.
{42}
имеет только один элемент с типом int
, тогда он точно соответствует перегрузке void f(int)
. В то время как для void f(std::vector<int>)
требуется определяемое пользователем преобразование. Итак, void f(int)
будет рассмотрено здесь.
Есть ли какой-либо способ заставить компилятор выбрать
std::vector
перегрузку (без явного построенияstd::vector<int>{42})
даже для списков из 1 элемента?
В качестве обходного маневра вы можете поставить дополнительные фигурные скобки, чтобы заставить компилятор создать std::initializer_list<int>
, а затем забрать void f(std::vector<int>)
:
f({{42}});
Ответ №2:
Принудительная перегрузка std::vector
int main()
{
f(std::vector<int>{42}); // the vector overload is being picked up now
}
Почему не используется vector(initializer_list)
конструктор?
Предположим, что другой заголовок объявляет void f(std::set<int> v)
.
Как бы вы хотели, чтобы компилятор реагировал, столкнувшись с f({1})
: construct a vector
или сконструировать set
?
Комментарии:
1. Черт, я забыл упомянуть, что без приведения отредактировал вопрос… Да, это действительно работает, но выглядит неуклюже. Но, вероятно, это единственный способ, поскольку
{2}
кажется, что он обрабатывается как int.2. @vsoftco придирка: это не приведение, это объект явного типа, который создается на месте. Точность сопряжена с риском быть более подробной даже в реальной жизни.
3. Да, вы абсолютно правы, я изменил вопрос.
4. @vsoftco «И что еще более странно, когда вы добавляете перегрузку std::initializer_list<int>, она превзойдет и с радостью примет {42} в качестве init-list» Что именно в этом такого странного? Это идеальное совпадение без какой-либо двусмысленности!
5. @vsoftco «Проблема в том, что {42} обрабатывается не как список, а как int, и я не понимаю почему». Я тоже не знаю (я не специалист по языкам), но я вижу, что без
std::initializer_list<int>
перегрузки ваша формулировка неоднозначна.