Подтвердить что ты не робот

Почему предпочтительнее использовать конструктор std:: initializer_list при использовании скопированного списка инициализаторов?

Рассмотрим код

#include <iostream>

class Foo
{
    int val_;
public:
    Foo(std::initializer_list<Foo> il)
    {
        std::cout << "initializer_list ctor" << std::endl;
    }
    /* explicit */ Foo(int val): val_(val)
    {
        std::cout << "ctor" << std::endl;
    };
};

int main(int argc, char const *argv[])
{
    // why is the initializer_list ctor invoked?
    Foo foo {10}; 
}

Выходной сигнал

ctor
initializer_list ctor

Насколько я понимаю, значение 10 неявно преобразуется в Foo (первый ctor вывод), тогда конструктор инициализатора запускает (второй initializer_list ctor вывод). Мой вопрос, почему это происходит? Не лучший ли стандартный конструктор Foo(int)? Я., я ожидал, что вывод этого фрагмента будет просто ctor.

PS: Если я отмечаю конструктор Foo(int) как explicit, тогда Foo(int) является единственным вызванным конструктором, поскольку целое число 10 теперь не может быть неявно преобразовано в Foo.

4b9b3361

Ответ 1

§13.3.1.7 [over.match.list]/p1:

Когда объекты типа неагрегатного класса T инициализируются списком (8.5.4), разрешение перегрузки выбирает конструктор в две фазы:

  • Изначально функции-кандидаты являются конструкторами-списками инициализаторов (8.5.4) класса T, а список аргументов состоит из список инициализаторов как один аргумент.
  • Если не найден жизнеспособный конструктор списка инициализаторов, снова выполняется разрешение перегрузки, где все функции-кандидаты конструкторы класса T и список аргументов состоит из элементы списка инициализаторов.

Если в списке инициализаторов нет элементов, а T имеет значение по умолчанию конструктор, первая фаза опущена. В инициализации списка копий, если выбран конструктор explicit, инициализация плохо сформирован.

Пока существует жизнеспособный конструктор-список инициализаторов, он будет превзойти все конструкторы-неинициализаторы-списки, когда используется инициализация списка, и список инициализаторов имеет хотя бы один элемент.

Ответ 2

Предложение n2100 для списков инициализаторов подробно описывает решение о создании конструкторов последовательностей (то, что они называют конструкторами, принимающими std::initializer_lists), чтобы иметь приоритет над регулярными конструкторами. См. Приложение B для подробного обсуждения. Он кратко изложил в заключении:

11.4 Заключение

Итак, как мы решаем между оставшимися двумя альтернативами ( "двусмысленность" и "конструкторы последовательностей имеют приоритет над обычными конструкторами)? В нашем предложении представлены конструкторы последовательностей приоритет, потому что

  • Поиск двусмысленностей среди всех конструкторов приводит к слишком большому количеству" ложных срабатываний"; то есть столкновения между явно не связанными Конструкторы. См. Примеры ниже.
  • Несознание само по себе подвержено ошибкам (а также многословным). См. Примеры в §11.3.
  • Важным является использование одного и того же синтаксиса для каждого числа элементов однородного списка - значения должны быть сделаны для обычные конструкторы (которые не имеют регулярной схемы аргументы). См. Примеры в п. 11.3. Простейший пример ложного положительным является конструктор по умолчанию:

Простейшим примером ложного положительного является конструктор по умолчанию:

vector<int> v; 
vector<int> v { }; // potentially ambiguous
void f(vector<int>&); 
// ...
f({ }); // potentially ambiguous

Можно подумать о классах, где инициализация без члены семантически отличаются от инициализации по умолчанию, но мы не усложняли бы язык, чтобы обеспечить лучшую поддержку для этих случаев, чем для более распространенного случая, когда они семантически то же самое.

Приоритет конструкторам последовательностей прерывает проверку аргументов более понятные куски и дает лучшую локальность.

void f(const vector<double>&);
// ...
struct X { X(int); /* ... */ };
void f(X);
// ...
f(1);     // call f(X); vector’s constructor is explicit
f({1});   // potentially ambiguous: X or vector?
f({1,2}); // potentially ambiguous: 1 or 2 elements of vector

Здесь приоритет для конструкторов последовательностей устраняет помехи от X. Выбор X для f (1) является вариантом проблемы с явным образом показано в п. 3.3.

Ответ 3

Вся информация о списке инициализаторов была предназначена для включения инициализации списка следующим образом:

std::vector<int> v { 0, 1, 2 };

Рассмотрим случай

std::vector<int> v { 123 };

То, что это инициализирует вектор с одним элементом значения 123, а не 123 элементами с нулевым значением.

Чтобы получить доступ к другому конструктору, используйте старый синтаксис

Foo foo(10);