Почему не удается частично инициализировать класс и затем вызывать делегирующий ctor? - PullRequest
1 голос
/ 20 сентября 2019

Следующий код не инициализирует элементы struct string с одинаковыми значениями.

#include <string>
#include <iostream>

struct S
{
    std::string s1;
    std::string s2;
    S(std::string const& s) : s1{s}{}
    S(int i) : S([&]{
        s2 = std::to_string(i);
        return std::to_string(i);
    }())
    {}
};

int main()
{
    S s{123};
    std::cout << s.s1 << "|" << s.s2;

    return 0;
}

Я получаю ошибку сегментации в gcc (пробовал разные версии) и 123| в clang (также разные версии) черезWandbox.

Я получаю нарушение прав чтения Visual Studio 15.9.16

Спасибо.

Ответы [ 2 ]

6 голосов
/ 20 сентября 2019

Аргумент, который вы даете конструктору (то есть лямбде), не может получить доступ к внутренним элементам, потому что они еще не были созданы.

Это должно работать:

struct S
{
    std::string s1;
    std::string s2;
    S(std::string const& s) : s1{s}{}
    S(int i)
      : S(std::to_string(i))
    {
        s2 = s1;
    }
};
1 голос
/ 20 сентября 2019

Проверка неинициализированного объекта - это UB, в данном случае он был скрыт этой строкой:

       s2 = std::to_string(i);

Это вызов operator= из std::string для объекта, хранящегося в местоположении s2.Это еще не было инициализировано.Я предполагаю, что вы могли бы это исправить, инициализировав s2, но это невозможно сделать до вызова конструктора в списке инициализации.

Вы не можете изменить порядок создания, даже если вы напишите этот раздел в неправильном порядке.s2 всегда инициализируется после s1, оба выполняются после вызова конструктора, но до ввода его тела.Таким образом, порядок выглядит так:

  1. Вызов ctor S(int) с инициализацией аргумента i;
  2. Создание лямбда-объекта, захват this и i
  3. Вызов оператора () лямбды
  4. Вызов operator= из std::string для объекта, хранящегося в местоположении s2
  5. Создание std::string
  6. Делегирование вызова ctor S(std::string) с аргументом s со ссылкой на созданную строку.
  7. Инициализация s1 со значением s
  8. Инициализация по умолчанию s2

Маркеры 4. и 8. выполняются в том порядке, который недопустим, даже хуже, из-за реализации он может не выдавать ошибку, поэтомуВерсия gcc, вероятно, записывает эти данные в какую-то случайную область памяти.MSVC, по крайней мере в отладочной версии, может генерировать некоторый диагностический код, поскольку стандарт не определяет поведение программы в этом случае.

...