繁体   English   中英

std::launder 和严格的别名规则

[英]std::launder and strict aliasing rule

考虑这个代码:

void f(char * ptr)
{
    auto int_ptr = reinterpret_cast<int*>(ptr); // <---- line of interest 
    // use int_ptr ... 
}

void example_1()
{
    int i = 10;    
    f(reinterpret_cast<char*>(&i));
}

void example_2()
{
    alignas(alignof(int)) char storage[sizeof(int)];
    new (&storage) int;
    f(storage);
}

来自example_1调用的兴趣线:

Q1:在调用方, char指针是我们的整数指针的别名。 这是有效的。 但是将它转换回int也有效吗? 我们知道一个int在它的生命周期内,但考虑到这个函数是在另一个翻译单元中定义的(没有启用链接时优化)并且上下文是未知的。 然后编译器看到的是:一个int指针想要给一个char指针取别名,这违反了严格的别名规则。 那么允许吗?

Q2:考虑到这是不允许的。 我们在 C++17 中得到了std::launder 这是一种主要用于访问的对象,安置了一个指针优化屏障new “编辑成其他类型或当一个对象的存储const成员参与。 我们可以用它来给编译器一个提示并防止未定义的行为吗?

来自example_2的调用的兴趣线:

Q3:这里应该需要std::launder ,因为这是 Q2 中描述的std::launder用例,对吗?

auto int_ptr = std::launder(reinterpret_cast<int*>(ptr));

但是再次考虑f是在另一个翻译单元中定义的。 编译器如何知道我们的布局new ,它发生在调用方? 编译器(只看到函数f )如何区分example_1example_2 或者以上只是假设,因为严格的别名规则只会排除一切(记住,不允许使用char*int* )并且编译器可以做它想做的事?

后续问题:

Q4:如果由于别名规则,以上所有代码都是错误的,请考虑更改函数f以采用空指针:

void f(void* ptr)
{
    auto int_ptr = reinterpret_cast<int*>(ptr); 
    // use int_ptr ... 
}

然后我们没有别名问题,但仍然存在example_2std::launder情况。 我们是否更改了调用方并将我们的example_2函数重写为:

void example_2()
{
    alignas(alignof(int)) char storage[sizeof(int)];
    new (&storage) int;
    f(std::launder(storage));
}

或者函数f std::launder足够?

严格别名规则是对实际用于访问对象的泛左值类型的限制。 对于该规则而言,所有重要的是 a) 对象的实际类型,以及 b) 用于访问的泛左值的类型。

指针经过的中间转换是无关紧要的,只要它们保留指针值即可。 (这是双向的;就这一点而言,再多的巧妙转换——或洗钱——都无法解决严格的别名违规问题。)

只要ptr实际上指向int类型的对象, f就是有效的,假设它通过int_ptr访问该对象而无需进一步转换。

example_1是有效的; reinterpret_cast不会改变指针值。

example_2是无效的,因为它给f一个实际上并不指向int对象的指针(它指向storage数组的生命周期外的第一个元素)。 请参阅放置 new 的返回值与其操作数的强制转换值之间是否存在(语义)差异?

您不需要在函数 f() 中使用 std::launder。 尽可能通用,函数 f() 可以与任何指针一起使用:脏(需要清洗)或不。 它只在呼叫方知道,所以你必须使用这样的东西:

void example_2()
{
    alignas(alignof(int)) char storage[sizeof(int)];
    new (&storage) int;        // char[] has gone, now int there
    f(std::launder(reinterpret_cast<int*>(storage)));  // assiming f(void* p)
}

而 f() 本身又是另一回事。 正如您所提到的,请考虑将 f() 放在共享库中,因此根本没有任何上下文假设。

暂无
暂无

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

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