std :: launder和严格别名规则

phö*_*hön 7 c++ strict-aliasing placement-new language-lawyer c++17

考虑以下代码:

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);
}
Run Code Online (Sandbox Code Playgroud)

感兴趣的行与example_1的调用:

Q1:在调用端,char指针使整数指针别名化.这是有效的.但是将它强制转换为int也是有效的吗?我们知道int在其生命周期内,但考虑该函数是在另一个转换单元中定义的(未启用链接时优化)并且上下文未知.然后所有编译器看到的是:int指针想要为char指针设置别名,这违反了严格的别名规则.允许这样吗?

Q2:考虑到不允许.我们在C++ 17中得到了std :: launder.它的一种指针优化屏障主要用于访问一个对象,该对象将新的位置放入其他类型的对象的存储中或者当涉及const成员时.我们可以用它来给编译器一个提示并防止未定义的行为吗?

从example_2调用的兴趣行:

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

auto int_ptr = std::launder(reinterpret_cast<int*>(ptr));
Run Code Online (Sandbox Code Playgroud)

但是再次考虑f在另一个翻译单元中定义.编译器如何知道我们的新位置,这发生在呼叫端?编译器(仅查看函数f)如何区分example_1和example_2?或者仅仅是低调的,因为严格的别名规则只会排除所有内容(记住char*to int*不允许)并且编译器可以做他想做的事情?

跟进问题:

Q4:如果由于别名规则导致上述所有代码都出错,请考虑更改函数f以获取void指针:

void f(void* ptr)
{
    auto int_ptr = reinterpret_cast<int*>(ptr); 
    // use int_ptr ... 
}
Run Code Online (Sandbox Code Playgroud)

然后我们没有别名问题,但仍然有这种std::launder情况example_2.我们是否更改了呼叫端并将我们的example_2功能重写为:

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

std::launder功能是否f足够?

T.C*_*.C. 12

严格别名规则是对实际用于访问对象的glvalue类型的限制.对于该规则而言最重要的是a)对象的实际类型,以及b)用于访问的glvalue的类型.

指针穿过的中间转换是无关紧要的,只要它们保留指针值即可.(这有两个方面;没有多少聪明的演员 - 或者洗钱,就此而言 - 将治愈严格的走样违规.)

f只要ptr实际指向类型的对象int,它就是有效的,假设它在int_ptr没有进一步强制转换的情况下访问该对象.

example_1有效; 该reinterpret_cast不会变化的指针值.

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