Стандарт определяет инициализацию списка с помощью лишних скобок (например, T {{{10}}})? - PullRequest
0 голосов
/ 02 ноября 2018

При использовании braced-init-list, содержащего несколько braced-init-list, какие правила определены стандартом для B, C и D?

Для B, я полагаю, что этот сценарий определен в стандарте как фигурный список инициализации с одним элементом, и поэтому он вызывает Test(int) напрямую, без временного - но я не могу найти где.

Для C и D я не уверен, является ли это неопределенным поведением или нет.

Меня также интересует, что происходит, когда используется более одного элемента, т.е. {{{1, 2}}} и меняет ли это поведение для B, C или D?

#include <iostream>

struct Test {
    Test(const int a) {
        // A and B call this
    }

    Test(Test&& test) = delete;
    Test(const Test& test) = delete;
};

int main()
{
    Test a{1}; // calls Test(int)
    Test b{{2}}; // B
    Test c{{{3}}}; // C
    Test d{{{{4}}}}; // D
    // Test e{a}; error, deleted copy constructor
    // Test f{Test{0}}; error, deleted move constructor
    return 0;
}

GCC g++ my_prog.cpp выдает ошибку только для C и D:

my_prog.cpp: In function 'int main()':
my_prog.cpp:16:17: error: too many braces around initializer for 'int' [-fpermissive]
     Test c{{{3}}};
                 ^
my_prog.cpp:4:14: note:   initializing argument 1 of 'Test::Test(int)'
     Test(int a) {
          ~~~~^

1 Ответ

0 голосов
/ 02 ноября 2018

Когда у вас есть

Test b{{2}}; 

[dcl.init.list] /3.7 состояний.

В противном случае, если T является типом класса, учитываются конструкторы. Применимые конструкторы перечисляются, и лучший выбирается через разрешение перегрузки ([over.match], [over.match.list]). [...]

и, глядя в [over.match], мы имеем [over.match.ctor] / 1

Когда объекты типа класса инициализируются напрямую, инициализируются копией из выражения того же или производного типа класса ([dcl.init]) или инициализируются по умолчанию, при разрешении перегрузки выбирается конструктор. Для прямой инициализации или инициализации по умолчанию, которая не находится в контексте инициализации копирования, все функции-кандидаты являются конструкторами класса инициализируемого объекта. Для инициализации копирования (включая инициализацию по умолчанию в контексте инициализации копирования) все функции-кандидаты являются конструкторами преобразования ([class.conv.ctor]) этого класса. Список аргументов является списком выражений или выражением присваивания инициализатора.

Итак, рассмотрим конструкторы, найдите

Test(const int a)

и затем мы используем элемент {2} в качестве инициализатора для a, который использует [dcl.init.list] /3.9

В противном случае, если в списке инициализатора есть один элемент типа E и либо T не является ссылочным типом, либо его ссылочный тип связан со ссылкой на E, объект или ссылка инициализируются из этого элемента (путем копирования-инициализации для copy-list-initialization или путем прямой инициализации для прямой инициализации списка); если для преобразования элемента в T требуется сужающее преобразование (см. ниже), программа некорректна.

С

Test c{{{3}}};
// and
Test d{{{{4}}}};

мы делаем то же самое. Смотрим на конструкторов и находим

Test(const int a)

как единственный жизнеспособный. Когда мы делаем и пытаемся инициализировать a, мы снова смотрим [dcl.init.list] /3.9, но здесь это не применимо. {{3}} и {{{4}}} не являются списками инициализаторов с одним типом E. У braced-init-list нет типа, поэтому мы должны продолжать список в [dcl.init.list] / 3 . Когда мы это делаем, мы не встречаем ничего, что соответствует до [dcl.init.list] /3.12

В противном случае программа некорректна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...