Принудительная перегрузка std :: vector вместо перегрузки int в списке одним элементом

#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
}
  

Live on Coliru

Я был немного удивлен, осознав, что в этом случае происходит перегрузка 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> перегрузки ваша формулировка неоднозначна.