Все версии GCC борются с инициализатором членов по умолчанию, который захватывает это, в сочетании с унаследованными конструкторами
Эта история похожа на мой предыдущий вопрос. Все версии GCC, поддерживающие C++11, имеют именно такое поведение. Я не смог найти другой компилятор, который борется с моим тестовым примером.
Контрольный пример:
struct BaseFooWrapper
{
BaseFooWrapper(int qux)
{ }
};
struct Foo
{
Foo(BaseFooWrapper & foo)
: foo(foo)
{ }
BaseFooWrapper & foo;
};
struct SomeFooWrapper : public BaseFooWrapper
{
using BaseFooWrapper::BaseFooWrapper;
Foo foo{*this};
};
int main()
{
SomeFooWrapper wrapped_foo(1);
return 0;
}
Жить на godbolt.com
Этот фрагмент кода компилируется с помощью clang (с 3.4 по 4.0), icc (16, 17), Visual C++ (19.00.23506).
Если я заменяю наследование конструктора рукописной версией, то GCC начинает компилировать код:
struct BaseFooWrapper
{
BaseFooWrapper(int qux)
{ }
};
struct Foo
{
Foo(BaseFooWrapper & foo)
: foo(foo)
{ }
BaseFooWrapper & foo;
};
struct SomeFooWrapper : public BaseFooWrapper
{
SomeFooWrapper(int qux)
: BaseFooWrapper(qux)
{ }
Foo foo{*this};
};
int main()
{
SomeFooWrapper wrapped_foo(1);
return 0;
}
Жить на godbolt.com
Очевидно, что это не очень удобно, особенно когда у вас много таких классов, и приводит к шаблонному коду. По сути, именно то, что наследующие конструкторы предназначены для исправления. Такое поведение GCC делает эту замечательную функцию C++11 недоступной в таких случаях.
Так что мне действительно любопытно, делаю ли я что-то незаконное в отношении стандарта или это ошибка в GCC?
Редактировать:
2 ответа
Проблема не в наследовании конструктора, а в следующей строке:
Foo foo{*this};
Похоже, что GCC думает, что ему также понадобится конструктор по умолчанию для struct Foo, и, поскольку у класса есть ссылка, он не может этого сделать.
error: no matching function for call to 'Foo::Foo()'
<source>:14:5: note: candidate: Foo::Foo(BaseFooWrapper&)
Foo(BaseFooWrapper & foo): foo(foo)
^~~
<source>:14:5: note: candidate expects 1 argument, 0 provided
<source>:10:7: note: candidate: constexpr Foo::Foo(const Foo&)
При добавлении конструктора по умолчанию кажется, что он нуждается в этом, а затем код компилируется:
struct Foo
{
Foo():foo(*new BaseFooWrapper(0))
{
}
Foo(BaseFooWrapper & foo): foo(foo)
{
}
BaseFooWrapper & foo;
};
Это похоже на ошибку.
Я могу ошибаться, но цитирую черновик стандарта n4527 в [class.this]:
В теле нестатической (9.3) функции-члена ключевое слово this является выражением prvalue, значением которого является адрес объекта, для которого вызывается функция. Тип этого в функции-члене класса X - X*.
В коде ОП инициализация SomeFooWrapper::foo
не происходит в функции члена. Поэтому ключевое слово this
не имеет разумного значения в отношении стандарта.
Или я что-то пропустил?