Почему `vector <int> v {{5,6}};` работает? Я думал, что разрешена только одна пара {}. - программирование

Почему `vector <int> v {{5,6}};` работает? Я думал, что разрешена только одна пара {}.

Учитывая класс A с двумя конструкторами, взяв initializer_list<int> и initializer_list<initializer_list<int>> соответственно, тогда

A v{5,6};

вызывает первое, а

A v{{5,6}};

вызывает последнее, как и ожидалось. (clang3.3, по-видимому, gcc ведет себя по-разному, см. ответы. Что требует стандарт?)

Но если я удалю второй конструктор, то A v{{5,6}}; все еще компилируется и использует первый конструктор. Я этого не ожидал. Я думал, что A v{5,6} будет единственным способом доступа к конструктору initializer_list<int>.

(я обнаружил это во время игры с std::vector и этим вопросом, который я задал Reddit, но я создал свой собственный класс A, чтобы быть уверенным, t просто причуда интерфейса для std::vector.)

4b9b3361

Ответ 1

Я думаю, что этот ответ может быть уместным.

Да, это поведение предназначено, согласно §13.3.1.7 Инициализация по инициализации списка

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

     

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

     

- Если не найден жизнеспособный конструктор списка инициализаторов, снова выполняется разрешение перегрузки, где все функции-кандидаты   конструкторы класса T и список аргументов состоят из   элементы списка инициализаторов.

В gcc Я попробовал ваш пример. Я получаю эту ошибку:

error: call of overloaded 'A(<brace-enclosed initializer list>)' is ambiguous

gcc перестает жаловаться, если я использую три набора фигурных скобок. то есть:.

#include <iostream>
#include <vector>
#include <initializer_list>

struct A {
    A (std::initializer_list<int> il) { 
        std::cout << "First." << std::endl;
    }
    A (std::initializer_list<std::initializer_list<int>> il) { 
        std::cout << "Second." << std::endl;
    }
};

int main()
{
    A a{0}; // first
    A a{{0}}; // compile error
    A a2{{{0}}}; // second
    A a3{{{{0}}}}; // second
}

В попытке отразить векторные конструкторы, вот мои результаты:

#include <iostream>
#include <vector>
#include <initializer_list>

struct A {
    A (std::initializer_list<int> il) { 
        std::cout << "First." << std::endl;
    }
    explicit A (std::size_t n) {
        std::cout << "Second." << std::endl;
    }
    A (std::size_t n, const int& val) {
        std::cout << "Third." << std::endl;
    }
    A (const A& x) {
        std::cout << "Fourth." << std::endl;
    }
};

int main()
{
    A a{0};
    A a2{{0}};
    A a3{1,2,3,4};
    A a4{{1,2,3,4}};
    A a5({1,2,3,4});
    A a6(0);
    A a7(0, 1);
    A a8{0, 1};
}

main.cpp:23:10: warning: braces around scalar initializer

    A a2{{0}};

         ^~~

1 warning generated.

First.
First.
First.
First.
First.
Second.
Third.
First.