Почему инициализация в фигурных скобках C ++ 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) {}

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

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

12

Решение

Короткий ответ: Это ошибка в Стандарте, которая исправлена ​​в 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 имеет абстрактный тип.

13

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


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