在从构造函数跳闸 UB 放置 new 后使用初始化变量?

Using initialized variable after placement new from the constructor tripping 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 实际上是 class 的一部分,它执行配置)

这个:

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

是未定义的行为。 r 上没有 R 类型的对象。句号。如果您翻转两条线以便首先创建 R,那么您就可以了(模 r 未充分对齐)。

或者真的,只是:

R r;
r.foo = returns_some_valid_foo();
return r;