Почему Foo ({}) вызывает Foo (0) вместо Foo ()?

Исполняемые файлы, созданные clang 3.5.0 и gcc 4.9.1 из кода

#include <iostream>

struct Foo
{
Foo() { std::cout << "Foo()" << std::endl; }
Foo(int x) { std::cout << "Foo(int = " << x << ")" << std::endl; }
Foo(int x, int y) { std::cout << "Foo(int = " << x << ", int = " << y << ")" << std::endl; }
};

int main()                 // Output
{                          // ---------------------
auto a = Foo();         // Foo()
auto b = Foo(1);        // Foo(int = 1)
auto c = Foo(2, 3);     // Foo(int = 2, int = 3)
auto d = Foo{};         // Foo()
auto e = Foo{1};        // Foo(int = 1)
auto f = Foo{2, 3};     // Foo(int = 2, int = 3)
auto g = Foo({});       // Foo(int = 0)          <<< Why?
auto h = Foo({1});      // Foo(int = 1)
auto i = Foo({2, 3});   // Foo(int = 2, int = 3)
}

вести себя как комментируется.

От cppreference: инициализация cpp / language / list:

[...]

T( { arg1, arg2, ... } )    (7)

[...]

Эффекты инициализации списка объекта типа T:

Если T является агрегатным типом, инициализация агрегата выполняется.

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

[...]

Я пришел к выводу, что Foo({}) должен вызвать конструктор по умолчанию.

Где ошибка?

15

Решение

Конструктор по умолчанию применим только в том случае, если вы используете одну пару скобок:

auto a = Foo();         // Foo()
auto b = Foo{};         // Foo()

Foo({}) вместо этого будут вызываться только конструкторы с пустым списком в качестве аргумента, copy-list-initializing для параметра любого выбранного конструктора. [Dcl.init] / 16:

Если тип назначения является (возможно, cv-квалифицированным) типом класса:
— Если
инициализация — конструкторы прямой инициализации […]. Применимые конструкторы
перечисляются (13.3.1.3), а лучший выбирается через перегрузку
разрешение (13,3). Выбранный конструктор называется
инициализировать объект с помощью выражения инициализатора или
список_выражений в качестве аргумента (ов).
Если конструктор не применяется, или
разрешение перегрузки неоднозначно, инициализация
плохо сформирован.

У вас есть один аргумент: пустой braced-init-list. Есть преобразование последовательности инициализации списка {} в int так что конструктор Foo(int) выбирается разрешением перегрузки. Параметр инициализируется в ноль как {} подразумевает Значение-intialization что для скаляров подразумевает нулевой инициализации.

Также нет ошибок в документации по cppreferences: для (7) указано, что

7) в функциональном приведенном выражении или другой прямой инициализации,
с braced-init-list, используемым в качестве аргумента конструктора

Это явно приводит к тому же результату, что и в приведенной выше цитате: конструктор вызывается с помощью (пустого) фигурного-инициализированного списка.

18

Другие решения


По вопросам рекламы [email protected]