简体   繁体   English

从构造函数触发UB放置新元素后使用初始化变量吗?

[英]Using initialized variable after placement new from the constructor tripping UB?

Disregarding whether the following can be achieved through other, more safe constructs - I'm simply interested in whether or not the following results in a well-defined output. 不管以下内容是否可以通过其他更安全的结构来实现-我只是对以下内容是否会产生明确定义的输出感兴趣。

Assume you have a struct A: 假设您有一个结构A:

struct A {
    Foo* foo;    
}

And a struct B inheriting from it: 还有一个结构B继承自它:

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

Sure enough if you were creating a B instance the normal way you'd trip UB, however... 当然,如果您以正常的方式创建UB来创建B实例,那么...

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?

Sheepishly assuming that C++ works like C somewhere under the hood, I'm guessing that this would be similar to having a struct instance in C, initializing it by hand, and then calling some method (in this case B's constructor) on the finished product. 令人毛骨悚然地假设C ++在幕后某个地方像C一样工作,我猜测这类似于在C中拥有一个struct实例,手动对其进行初始化,然后在最终产品上调用某种方法(在这种情况下为B的构造函数) 。

Again, I'm not interested in whether you should do this or not, it's just a technical question. 同样,我对您是否应该执行此操作不感兴趣,这只是一个技术问题。

EDIT: 编辑:

If you wonder what this could be useful for, I could use it to pull out values into a plain struct from, say, a configuration file in a really terse manner. 如果您想知道这可能有什么用,我可以使用它以非常简洁的方式将值从(例如)配置文件中提取到纯结构中。 Yes it does use macros but call it a stub until C++ gets compile time reflection: 是的,它确实使用宏,但是将其称为存根,直到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>();

(Using A and foo here to stay consistent with what I wrote above, make_A is actually part of a class that does config) (在这里使用A和foo与我上面写的保持一致,make_A实际上是进行配置的类的一部分)

This: 这个:

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

is undefined behavior. 是未定义的行为。 There is no object of type R at r . r处没有类型R对象。 Full stop. 句号 If you flip the two lines so that you create the R first, then you're fine (modulo r being insufficiently aligned). 如果翻转两条线以便首先创建R ,那么就没问题了(模数r对齐不充分)。

Or really, just: 或者说真的:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM