Чем инициализация {} в списке инициализации конструктора отличается от инициализации () при инициализации ссылки на абстрактные типы? Возьмите класс 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) {}
он компилируется и работает нормально.
Читая книгу Страуструпа по C ++ 11, у меня сложилось впечатление, что {} был таким же, как () в большинстве случаев, за исключением случаев, когда между конструкторами, принимающими std :: initializer_list, была неоднозначность<> и другие конструкторы, и случаи, когда в качестве типа используется auto, чего я здесь не делаю.
Короткий ответ: Это ошибка в Стандарте, которая исправлена в C ++ 14, а в g ++ 4.9 есть исправление (задним числом применяется и в режиме C ++ 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
В тексте C ++ 11 значение S const &y {x};
не связывать y
в x
; на самом деле смысл заключается в том, чтобы создать временный и привязать ссылку на это. Из C ++ 11 [dcl.init.ref] / 3:
В противном случае, если T является ссылочным типом, временное значение типа, на который ссылается T, инициализируется списком, и ссылка привязывается к этому временному объекту. [Примечание: Как обычно, привязка завершится неудачно, и программа будет некорректной, если ссылочный тип является lvalue-ссылкой на неконстантный тип. —Конечная записка]
Это довольно глупо, ясно, что цель этого кода — связать y
прямо к x
, В C ++ 14 текст был изменен:
В противном случае, если список инициализатора имеет единственный элемент типа E и либо T не является ссылочным типом, либо его ссылочный тип связан со ссылкой на E, объект или ссылка инициализируются из этого элемента;
Поскольку тип связан со ссылкой на самого себя (или один из его базовых классов), в моем примере здесь и в вашем реальном коде он должен на самом деле правильно связываться.
Ваше сообщение об ошибке приходит от компилятора после формулировки C ++ 11 и пытается создать временный из base
привязать ссылку к; и это не удается, потому что base
имеет абстрактный тип.