Делегирующий конструктор дает ошибку сегментации при использовании поля класса для аргумента

Фактически ошибка сегментации происходит в другой программе, которую я пытался скомпилировать, которая происходит из-за этого поведения.

Мой вопрос:

Это ошибка или моя ошибка?

Воспроизводится любым способом (даже если поле something является приватным или защищенным), и вот мой пример:

main.cc:

#include <iostream>
class Test {
    public:
        const char* something = "SOMETHING HERE!!!";
        Test(const int& number) : Test(something, number) { }
        // XXX: changed `something` to `_something` to make it different
        Test(const char* _something, const int& number) {
            std::cout << _something << std::endl;
            std::cout << number << std::endl; }
        ~Test() { }
};

int main(int argc, char* argv[]) {
    Test te1(345);
    Test te2("asdasdad", 34523);
    return 0;
}

И вот что происходит при компиляции с помощью:

g++ main.cc -Os -o main

и работает с:

./main

вывод:

[email protected]:~/ $ ./main
A"�~ <-- this is random
345
asdasdad
34523

Но когда я включаю оптимизацию с помощью -O0 или -O1 или -O2... вывод представляет собой только новую строку:

[email protected]:~/ $ ./main
[email protected]:~/ $

Версия g++:

[email protected]:~/ $ g++ --version
g++ (Raspbian 6.3.0-18+rpi1) 6.3.0 20170516

Ответ 1

const char* something = "SOMETHING HERE!!!";

Инициализатор по умолчанию справа, как следует из его имени, используется только тогда, когда вы не предоставляете явный инициализатор в списке инициализаторов конструктора. Давайте посмотрим на ваши:

Test(const int& number) : Test(something, number) { }

Хорошо, мы делегируем другой конструктор. Этот другой конструктор выполнит полную инициализацию, поэтому инициализатор по умолчанию не используется. Но... мы передаем неинициализированное значение something в качестве параметра.

Test(const char* _something, const int& number) { /* ... */ }

О-оу. Теперь мы пытаемся использовать значение _something, которое является копией something, которая является неопределенной. Undefined Поведение и пожар.

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


Поведение, которое вы ищете, можно получить, поместив значение по умолчанию в вызов конструктору делегата:

Test(const int& number) : Test("SOMETHING HERE!!!", number) { }

... или сохранить его в заданной статической переменной:

static constexpr char *const defaultSomething = "SOMETHING HERE!!!";
Test(const int& number) : Test(defaultSomething, number) { }

Ответ 2

Это ошибка или моя ошибка?

О, это ваша вина. Инициализатор элемента по умолчанию используется только для инициализации объекта-члена в конструкторе без делегирования. Согласно [class.base.init]/9, мое внимание:

В конструкторе без делегаций, если данный потенциально сконструированный подобъект не обозначается идентификатором mem-initializer-id (включая случай, когда нет списка mem-initializer-list, поскольку конструктор не имеет ctor-инициализатора), то

  • если объект является нестатическим членом данных, у которого есть инициализатор элемента по умолчанию, и либо [...] объект инициализируется из его инициализатор элемента по умолчанию, как указано в [dcl.init];

Итак, something не инициализируется, когда вы передаете его целевому конструктору. Ваша программа имеет поведение undefined и идет спад.