LINUX.ORG.RU

Использование POD членов класса до их инициализации

 ,


1

3

Привет ЛОР,

Подскажите, наличествует ли в приведенном коде UB? https://wandbox.org/permlink/6aWOyqM3lln2aJgT

class StringView {
public:
    StringView(const char *str, std::size_t size)
        : m_str(str),
          m_size(size)
    {}
    
    const char *data() const { return m_str; }
    std::size_t size() const { return m_size; }
    
private:
    const char * const m_str;
    const std::size_t m_size;
};

template <typename T>
class NumericStringView : public StringView {
public:
   NumericStringView(T value) 
       // вопрос с топика адресует строчку ниже
       : StringView(convertToBuffer(m_buffer, BUFFER_SIZE, value))
   {}
    
private:
    static StringView convertToBuffer(char *buffer, std::size_t size, T value) {
        auto str = std::to_string(value);
        if (str.size() >= size)
            throw std::runtime_error("value is too big");
        
        strcpy(buffer, str.c_str());
        return StringView(buffer, str.size());
    }
    
private:
    constexpr static std::size_t BUFFER_SIZE = 30;
    // указатель на данный POD-член передается в родительский конструктор
    char m_buffer[BUFFER_SIZE]; 
};

А именно интересует вызов конструктора базового класса с (неявной) передачей ему указателя на POD член дочернего класса (StringView(convertToBuffer(m_buffer, BUFFER_SIZE, value))).

P.S. да, я знаю про std::string_view и в оригинальном коде он и используется. В данном примере StringView использован для наглядности (см. по ссылке).

P.P.S. В оригинале в convertToBuffer используется sprintf и черная магия, потому попрошу не придираться к реализаии в примере.

★★★★★

Последнее исправление: KennyMinigun (всего исправлений: 1)

Не могу найти описание подходящее под вопрос в стандарте: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf

Пока только нашел в 15.6 про порядок инициализации. Ну и ясно, m_buffer будет инициализирован после предка StringView. Однако, требует ли он инициализации?

KennyMinigun ★★★★★
() автор топика

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

anonymous
()

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

ckotinko ☆☆☆
()

Заменить этот кусок:

constexpr static std::size_t BUFFER_SIZE = 30;
    // указатель на данный POD-член передается в родительский конструктор
char m_buffer[BUFFER_SIZE];

на этот:

std::string m_string;

Избавиться от ненужного:

if (str.size() >= size)
   throw std::runtime_error("value is too big");
        
strcpy(buffer, str.c_str());

andreyu ★★★★★
()
Ответ на: комментарий от andreyu

К сожалению std::string слишком тяжелый (даже с оптимизацией для «малых» строк): он делает аллокацию, а у меня NumericStringView будет ну очень часто дергаться (составление XML ответа). Класс NumericStringView создан чтоб не использовать std::to_string (опять же — пример очень упрощен, чтоб не читать простыню и работал).

Грубо говоря, NumericStringView — 30 байтов «прикрепленные» к std::string_view. И размещатся оно все будет на стеке.

Конечно была еще опция кастомного аллокатора для std::string, но оказалось, что в std::to_string не хватает некоторых нюансов. Вышло, что использовать std::string вообще нет смысла.

KennyMinigun ★★★★★
() автор топика
Последнее исправление: KennyMinigun (всего исправлений: 1)

Не должно быть. UB возникает при использовании неинициализированной памяти, а не передаче ссылки на неё, записи в неё или переинициализации. Для случаев с конструкторами есть Base from Member, тут тоже можно это применить, но кажется лишним.

xaizek ★★★★★
()

Чтобы совсем не было паранойи :)

struct buffer_holder {
  constexpr static std::size_t BUFFER_SIZE = 30;
  char m_buffer[ BUFFER_SIZE ];
};
...
template <typename T>
class NumericStringView : private buffer_holder, public StringView {
public:
   NumericStringView(T value)
       : StringView(convertToBuffer(m_buffer, BUFFER_SIZE, value))
   {}
   ...
};
К моменту вызова конструктора StringView содержимое buffer_holder-а уже будет полностью сконструировано.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Ага, как раз по паттерну из ссылочки выше

KennyMinigun ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.