Используя инициализированную переменную после размещения нового из конструктора, отключающего UB?

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

Предположим, у вас есть структура A:

struct A {
    Foo* foo;    
}

И структура B наследуется от него:

struct B : A {
    B() {
        foo->some_function(); // UB
    }
}

Конечно же, если вы создавали B Экземпляр нормальный способ, как вы бы отключить UB, однако...

template<typename R> 
R make_A() { // This acts like a constructor for As
    static_assert(std::is_base_of<A, R>::value, "R must derive from A");
    char r[sizeof(R)];
    ((R*)r)->foo = returns_some_valid_foo();
    new (r) R;

    return *((R*)r);
}

B b1; // Blows up (Could you somehow prevent this from compiling without changing B?)
B b2 = make_A<B>(); // Works fine?

Застенчиво предполагая, что C++ работает как C где-то под капотом, я предполагаю, что это было бы похоже на наличие экземпляра структуры в C, инициализацию его вручную, а затем вызов некоторого метода (в данном случае конструктора B) для готового продукта.,

Опять же, меня не интересует, стоит ли вам это делать или нет, это просто технический вопрос.

РЕДАКТИРОВАТЬ:

Если вам интересно, для чего это может быть полезно, я мог бы использовать его для извлечения значений в простую структуру, скажем, из файла конфигурации, действительно кратким образом. Да, он использует макросы, но называет его заглушкой, пока C++ не получит отражение во время компиляции:

#define config_key($x, $def) $x = foo->get<decltype($x)>(#$x, ($def))   

struct Record : A {
    int    config_key(a, 3); // Second parameter is default value
    string config_key(b, "something");
}

auto record = make_A<Record>();

(Используя A и foo здесь, чтобы соответствовать тому, что я написал выше, make_A на самом деле является частью класса, который выполняет конфигурацию)

1 ответ

Решение

Это:

((R*)r)->foo = returns_some_valid_foo();

является неопределенным поведением. Там нет объекта типа R в r, Полная остановка. Если вы переверните две строки, чтобы создать R во-первых, тогда ты в порядке (по модулю r недостаточно выровнен).

Или действительно, просто:

R r;
r.foo = returns_some_valid_foo();
return r;
Другие вопросы по тегам