Инициализатор Ctor: само инициализация вызывает сбой?

Мне пришлось тяжело отлаживать крах при производстве. Просто хотел подтвердить, что у нас здесь семантика. У нас есть класс вроде...

class Test {
public:
  Test()
  {
    // members initialized ...
    m_str = m_str;
  }
  ~Test() {}
private:
  // other members ...
  std::string m_str;
};

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

class Test {
public:
  Test() 
    : /*other inits ,,, */ m_str(m_str)
  {
  }
  ~Test() {}
private:
  // other members ...
  std::string m_str;
};

Но код внезапно начал сбой! Я выделил длинный список inits для этого фрагмента кода m_str(m_str). Я подтвердил это через текст ссылки.

Нужно ли это терпеть крах? Что говорит об этом стандарт? (Это поведение undefined?)

Ответ 1

Первый конструктор эквивалентен

  Test()
  : m_str()
  {
    // members initialized ...
    m_str = m_str;
  }

то есть к тому времени, когда вы перейдете к назначению внутри конструктора, m_str уже неявно инициализируется пустой строкой. Таким образом, присваивание себе, хотя и совершенно бессмысленное и лишнее, не вызывает проблем (поскольку std::string::operator=(), как и любой хорошо написанный оператор присваивания, должен проверять самоопределение и ничего не делает в этом случае).

Однако во втором конструкторе вы пытаетесь инициализировать m_str с самим собой в списке инициализаторов - в этот момент он еще не инициализирован. Таким образом, результат undefined.

Обновление:. Для примитивных типов это поведение undefined (в результате получается поле с размером мусора), но оно не падает (обычно - см. комментарии ниже для исключений), потому что примитивный типы по определению не имеют конструкторов, деструкторов и не содержат указателей на другие объекты.

То же самое верно для любого типа, который не содержит элементов указателя с семантикой собственности. std::string, как показано, не является одним из следующих: -)

Ответ 2

m_str строится в списке инициализации. Поэтому в то время, когда вы назначаете его себе, он не полностью построен. Следовательно, поведение undefined.

(Что должно делать это самозадача?)

Ответ 3

Исходная "инициализация" по назначению полностью лишняя.

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

Во втором фрагменте кода инициализация по умолчанию переопределяется, чтобы использовать элемент un-isitialized, который еще не инициализирован для инициализации. Это Undefined Поведение. И это совершенно не нужно: просто удалите это (и не заново вводите исходное время, просто, удалите).

Подняв уровень предупреждения вашего компилятора, вы сможете получить предупреждения об этом и подобном тривиально некачественном коде.

К сожалению, проблема, с которой вы сталкиваетесь, не такая техническая, она гораздо более фундаментальная. Он, как рабочий в автомобиле factory, задает вопрос о квадратных колесах, которые они наносят на новый автомобиль. Тогда проблема заключается не в том, что квадратные колеса не работают, а потому, что многие инженеры и менеджеры принимали участие в решении использовать фантастические квадратные колеса, и никто из них не возражал - некоторые из них, несомненно, Не понимаю, что квадратные колеса не работают, но большинство из них, я подозреваю, просто боялись сказать, на что они были на 100% уверены. Так что это, скорее всего, проблема управления. Извините, но я не знаю, как это исправить...

Ответ 4

Undefined поведение не должно приводить к сбою - он может делать что угодно, от продолжения работы, как если бы не было никакой проблемы, немедленно рушиться, делать что-то действительно странное, что, по-видимому, вызывает не связанные проблемы позже. Каноническая претензия заключается в том, что она заставляет "демонов вылетать из вашего носа" (ака, "вызывает носовых демонов" ). В свое время у изобретателя фазы был (довольно крутой) веб-сайт, рассказывающий о ядерной войне, которая началась от кого-то, вызывающего поведение undefined в "DeathStation 9000".

Изменить: точная формулировка стандарта (§: 1.3.12):

1.3.12 undefined поведение [defns.undefined]

например, может возникнуть при использовании ошибочной конструкции программы или ошибочных данных, для которой это Международный стандарт не предъявляет никаких требований. undefined также можно ожидать, когда это В Международном стандарте отсутствует описание любого явного определения поведения. [Примечание: допустимо undefinedповедение варьируется от полного игнорирования ситуации с непредсказуемыми результатами, поведения во время перевод или выполнение программы в документально оформленной форме, характерной для окружающей среды (с или без выдача диагностического сообщения), до прекращения перевода или исполнения (с выдачей диагностическое сообщение).

Ответ 5

Это та же разница, что и между

std::string str;
str = str;

и

std::string str(str);

Первый работает (хотя это вздор), последний не делает, поскольку он пытается скопировать-построить объект из еще не построенного объекта.

Конечно, путь был бы

Test() : m_str() {}