Разница в синтаксисе унифицированной инициализации

Какая разница между делать

A a{ A() };

а также,

A a( A{} );

чтобы избежать самого неприятного разбора? Когда я должен использовать конкретный?


person Me myself and I    schedule 31.05.2013    source источник
comment
В данной конкретной ситуации самым простым вариантом будет A a;, верно? Если я не ошибаюсь, предложенный вами синтаксис имеет смысл только тогда, когда временное значение, которое вы хотите передать конструктору A, имеет другой тип, чем A, верно? т.е. A a { B() };.   -  person jogojapan    schedule 04.06.2013


Ответы (2)


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

A a{ A{} };

В противном случае для устранения неоднозначности можно использовать только круглые скобки:

A a((A())); // This can't be parsed as a function declaration

Обратите внимание, что есть одна ситуация (очень маловероятная, я должен сказать), когда две формы, показанные в вашем вопросе, не эквивалентны. Если в вашем классе A есть конструктор, который принимает initializer_list<A>, этот конструктор будет предпочтительнее конструктора копирования при использовании фигурных скобок:

#include <initializer_list>
#include <iostream>

struct A
{
    A() { }
    A(std::initializer_list<A> l) { std::cout << "init-list" << std::endl; }
    A(A const& a) { std::cout << "copy-ctor" << std::endl; }
};

int main()
{
    A a(A{}); // Prints "copy-ctor" (or nothing, if copy elision is performed)
    A b{A()}; // Prints "init-list"
}

Вышеупомянутая разница показана в этом живом примере.

person Andy Prowl    schedule 31.05.2013
comment
На самом деле, первый не печатает для меня копировальный аппарат. Я думаю, что это копия-элизия. - person Me myself and I; 01.06.2013
comment
@MemyselfandI: Да, да, компилятор пропускает копию, но концептуально выбирается конструктор копирования - person Andy Prowl; 01.06.2013
comment
Происходит ли копирование-элизион только при включенной оптимизации? - person Me myself and I; 01.06.2013
comment
@MemyselfandI: Нет. Во многих компиляторах он всегда будет включен, если вы специально не отключите его. - person Mankarse; 01.06.2013
comment
@MemyselfandI: Не обязательно. Исключение копирования происходит полностью по усмотрению вашего компилятора, но более вероятно это произойдет, когда включена оптимизация. - person Andy Prowl; 01.06.2013
comment
@MemyselfandI: с помощью GCC и Clang вы можете заставить компилятор запрещать исключение копирования, передав флаг -fno-elide-constructors - person Andy Prowl; 01.06.2013
comment
Имейте в виду, что -fno-elide-constructors в настоящее время не работает на clang. - person Mankarse; 01.06.2013

В большинстве ситуаций они эквивалентны, но A a{ A() }; предпочтет конструктор std::initializer_list, если он присутствует, а A a( A{} ); предпочтет конструктор перемещения/копирования.

Когда конструкция завершается вызовом конструктора перемещения/копирования, конструкцию нового объекта можно пропустить, но это невозможно для конструктора std::initializer_list.

Ни один синтаксис никогда не будет разобран как объявление функции, поэтому оба избегают самого неприятного разбора.

#include <iostream>
#include <initializer_list>
struct A {
    A() {
        std::cout << "A()\n";
    }
    A(A&&) {
        std::cout << "A(A&&)\n";
    }
    A(std::initializer_list<A>) {
        std::cout << "A(std::initializer_list<A>)\n";
    }
};
int main()
{
    {A a{ A() };} // Prints "A()\n" "A(std::initializer_list<A>)\n"
    {A a( A{} );} // Prints "A()\n" and *possibly*
                  // (depending on copy elision) "A(A&&)\n"
}
person Mankarse    schedule 31.05.2013