Ответ 1
Foo foo{x}; // 1
Foo foo = {x}; // 2
1 - инициализация прямого списка. 2 - инициализация списка копий.
Предполагая, что Foo
является типом класса, тогда в большинстве случаев они делают то же самое, концептуально или иначе, за исключением того, что если явный конструктор выбран во время разрешения перегрузки, то # 2 плохо сформирован. В частности, в отличие от инициализации копирования, инициализация списка экземпляров концептуально не создает временную.
Единственное исключение - это когда x
имеет тип Foo
или тип, полученный из него. В этом случае # 1 эквивалентно Foo foo(x);
(т.е. Прямая инициализация), а # 2 эквивалентно Foo foo = x;
(т.е. Копирование-инициализация). Тонкая разница заключается в том, что разрешение перегрузки для # 2 в этом случае рассматривает только неявные конструкторы, а не рассматривает все конструкторы, а затем становится плохо сформированным, если выбран явный конструктор. * Это исключение добавляется разрешением CWG issue 1467, который был принят в ноябре прошлого года.
* Вам нужно написать какой-то довольно извращенный код, чтобы это имело значение. Например:
struct X
{
X() = default;
explicit X(X&);
X(const X&);
};
int main() {
X x1;
X x2 = {x1}; // #1
X x3 = x1; // #2
}
Pre-CWG1467, строка # 1 плохо сформирована, потому что разрешение перегрузки выбирает X(X&)
, что составляет explicit
. Post-CWG1467, конструктор explicit
X(X&)
не рассматривается, поэтому используется X(const X&)
. Обратите внимание, что строка # 2 всегда хорошо сформирована и использует X(const X&)
.