Почему не инициализация С++ 11 в фигурных скобках в списке инициализации конструктора работает при инициализации parens?

Как инициализация {} инициализации в списке инициализации конструктора отличается от() инициализации при инициализации ссылки на абстрактные типы? Возьмите класс Bar ниже:

class AbstractBase
{
public:
    AbstractBase() {}
    virtual ~AbstractBase() = default;

    virtual void ab() = 0;
};

class Foo : public AbstractBase
{
public:
    Foo() {}

    void ab() {}
};

class Bar
{
public:
    Bar(const AbstractBase& base) : myBase{base} {}

private:
    const AbstractBase& myBase;
};


int main()
{
    Foo f{};
    Bar b{f};

}

При компиляции я получаю сообщение об ошибке

test5.cpp: In constructor ‘Bar::Bar(const AbstractBase&)’:
test5.cpp:22:48: error: cannot allocate an object of abstract type ‘AbstractBase’
     Bar(const AbstractBase& base) : myBase{base}
                                                ^
test5.cpp:2:7: note:   because the following virtual functions are pure within ‘AbstractBase’:
 class AbstractBase
       ^
test5.cpp:8:18: note:   virtual void AbstractBase::ab()
     virtual void ab() = 0;

Изменение строки

Bar(const AbstractBase& base) : myBase(base) {}

он компилируется и работает нормально.

Прочитав книгу Stroustrup С++ 11, у меня сложилось впечатление, что {} совпадает с() в большинстве случаев, за исключением случаев, когда существует неоднозначность между конструкторами, которые принимают std:: initializer_list < > и другие конструкторы, и случаи, когда использование auto как типа, ни то, что я здесь делаю.

Ответ 1

Короткий ответ: Это была ошибка в стандарте, которая исправлена ​​в С++ 14, а g++ 4.9 имеет исправление (задним числом применяется также к режиму С++ 11). Отчет о дефектах 1288


Вот более простой пример:

struct S
{
    int x;
    S() { }     // this causes S to not be an aggregate (otherwise aggregate 
                // initialization is used instead of list initialization)
};

S x = 5;
S const &y { x } ;    

x = 6;
std::cout << y << std::endl;     // output : 5

В тексте С++ 11 значение S const &y {x}; не связывает y с x; на самом деле смысл состоит в том, чтобы создать временное и связать ссылку на это. Из С++ 11 [dcl.init.ref]/3:

В противном случае, если T является ссылочным типом, временное значение для типа, на которое ссылается T, инициализируется по списку, а ссылка привязана к этому временному. [Примечание. Как обычно, привязка завершится неудачно, и программа будет плохо сформирована, если ссылочный тип является ссылкой lvalue на неконстантный тип. -end note]

Это довольно глупо, ясно, что целью этого кода является привязка y непосредственно к x. В С++ 14 текст был изменен:

В противном случае, если в списке инициализаторов есть один элемент типа E, и либо T не является ссылочным типом, либо его ссылочный тип ссылается на E, объект или ссылка инициализируются из этого элемента;

Поскольку тип связан с привязкой к самому себе (или одному из его базовых классов), в моем примере здесь и в вашем фактическом коде, он должен фактически корректно связываться.


Ваше сообщение об ошибке приходит из компилятора, следующего за формулировкой С++ 11, и пытается создать временную ссылку из base для привязки ссылки; и это терпит неудачу, потому что base имеет абстрактный тип.