std ::洗钱和严格的别名规则 [英] std::launder and strict aliasing rule

查看:154
本文介绍了std ::洗钱和严格的别名规则的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码:

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的呼叫)

line of interest with call from example_1:

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

Q1: On the callside the char pointer is aliasing our integer pointer. This is valid. But is it also valid to just cast it back to an int? We know an int is within its lifetime there, but consider the function is defined in another translation unit (with no linktime optimization enabled) and the context is not known. Then all the compiler sees is: an int pointer wants to alias a char pointer, and this is violating the strict aliasing rules. So is it allowed?

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

Q2: Considering its not allowed. We got std::launder in C++17. Its kind of a pointer optimization barrier mostly used to access an object which got placement new'ed into the storage of an object of other type or when const members are involved. Can we use it to give the compiler a hint and prevent undefined behavior?

感兴趣的线路(来自example_2的呼叫)

line of interest with call from example_2:

第3季度:在这里应该需要std :: launder,因为这是第2季度中描述的std :: launder用例,对吧?

Q3: Here std::launder should be required, since this is the std::launder use case described in Q2, right?

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

但是请再次考虑在另一个翻译单元中定义了f.编译器如何知道我们在调用端发生的新放置?编译器(仅看到函数f)如何区分example_1和example_2?还是上述所有都是假设性的,因为严格的别名规则将排除所有内容(记住char *到int *不允许),并且编译器可以执行他想要的操作?

But consider again f is defined in another translation unit. How can the compiler know about our placement new, which happens on the callside? How can the compiler (only seeing function f) distinguish between example_1 and example_2? Or is all above just hypotetical, since the strict aliasing rule would just ruling out everything (remember char* to int* not allowed) and the compiler can do what he wants?

后续问题:

问题4:如果由于别名规则导致上述所有代码都不正确,请考虑更改函数f以使用空指针:

Q4: If all code above is wrong due to aliasing rules, consider changing the function f to take a void pointer:

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

那么我们就没有混叠问题,但是example_2仍然有std::launder的情况.我们是否更改了调用方并将我们的example_2函数重写为:

Then we have no aliasing problem, but still there is the std::launder case for example_2. Do we have change the callside and rewrite our example_2 function to:

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

还是功能f中的std::launder是否足够?

or is std::launder in function f sufficient?

推荐答案

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

The strict aliasing rule is a restriction on the type of the glvalue actually used to access an object. All that matters for the purpose of that rule are a) the actual type of the object, and b) the type of the glvalue used for the access.

只要指针保留指针值,中间经过的转换就无关紧要. (这是双向的;就此而言,进行任何巧妙的强制转换或洗钱都无法解决严格的混叠违规问题.)

The intermediate casts the pointer travels through are irrelevant, as long as they preserve the pointer value. (This goes both ways; no amount of clever casts - or laundering, for that matter - will cure a strict aliasing violation.)

f就有效,前提是它通过int_ptr访问该对象而无需进一步转换.

f is valid as long as ptr actually points to an object of type int, assuming that it accesses that object via int_ptr without further casting.

example_1按书面规定有效; reinterpret_cast不会更改指针值.

example_1 is valid as written; the reinterpret_casts do not change the pointer value.

example_2无效,因为它给f一个指针,该指针实际上并不指向int对象(它指向storage数组的寿命终止的第一个元素).参见位置new的返回值与其操作数的强制转换值之间是否有(语义上的)区别?

example_2 is invalid because it gives f a pointer that doesn't actually point to an int object (it points to the out-of-lifetime first element of the storage array). See Is there a (semantic) difference between the return value of placement new and the casted value of its operand?

这篇关于std ::洗钱和严格的别名规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆