reinterpret_cast与严格的别名 [英] reinterpret_cast vs strict aliasing

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

问题描述

我正在阅读严格的别名,但是它仍然有点模糊,而且我不确定所定义/未定义的行为在哪里.我发现最详细的帖子专注于C.因此,如果您可以告诉我是否允许这样做,以及自C ++ 98/11/...以来发生了什么变化.

I was reading about strict aliasing, but its still kinda foggy and I am never sure where is the line of defined / undefined behaviour. The most detailed post i found concentrates on C. So it would be nice if you could tell me if this is allowed and what has changed since C++98/11/...

#include <iostream>
#include <cstring>

template <typename T> T transform(T t);

struct my_buffer {
    char data[128];
    unsigned pos;
    my_buffer() : pos(0) {}
    void rewind() { pos = 0; }    
    template <typename T> void push_via_pointer_cast(const T& t) {
        *reinterpret_cast<T*>(&data[pos]) = transform(t);
        pos += sizeof(T);
    }
    template <typename T> void pop_via_pointer_cast(T& t) {
        t = transform( *reinterpret_cast<T*>(&data[pos]) );
        pos += sizeof(T);
    }            
};    
// actually do some real transformation here (and actually also needs an inverse)
// ie this restricts allowed types for T
template<> int transform<int>(int x) { return x; }
template<> double transform<double>(double x) { return x; }

int main() {
    my_buffer b;
    b.push_via_pointer_cast(1);
    b.push_via_pointer_cast(2.0);
    b.rewind();
    int x;
    double y;
    b.pop_via_pointer_cast(x);
    b.pop_via_pointer_cast(y);
    std::cout << x << " " << y << '\n';
}

请不要过多关注可能的越界访问,以及可能不需要编写类似内容的事实.我知道允许char*指向任何内容,但是我也有一个T*指向char*.也许还有其他我想念的东西.

Please dont pay too much attention to a possible out-of-bounds access and the fact that maybe there is no need to write something like that. I know that char* is allowed to point to anything, but I also have a T* that points to a char*. And maybe there is something else I am missing.

这是一个完整示例,其中还包括通过memcpy进行的推送/弹出,而afaik并非如此受严格混叠的影响.

Here is a complete example also including push/pop via memcpy, which afaik isn't affected by strict aliasing.

TL; DR:上面的代码是否表现出未定义的行为(暂时忽略了超出范围的访问权限),如果是,为什么? C ++ 11或较新的标准之一有什么变化吗?

TL;DR: Does the above code exhibit undefined behaviour (neglecting a out-of-bound acces for the moment), if yes, why? Did anything change with C++11 or one of the newer standards?

推荐答案

我知道允许char*指向任何内容,但是我也有一个T*指向char*.

I know that char* is allowed to point to anything, but I also have a T* that points to a char*.

对,那是一个问题.尽管指针强制转换本身已经定义了行为,但使用指针访问类型不存在的T对象却不是.

Right, and that is a problem. While the pointer cast itself has defined behaviour, using it to access a non-existent object of type T is not.

与C不同,C ++不允许即兴创建对象 * .您不能简单地将某个存储位置分配为类型T并创建该类型的对象,而您需要已经存在该类型的对象.这需要放置new.以前的标准对此尚不明确,但目前每个[intro.object]:

Unlike C, C++ does not allow impromptu creation of objects*. You cannot simply assign to some memory location as type T and have an object of that type be created, you need an object of that type to be there already. This requires placement new. Previous standards were ambiguous on it, but currently, per [intro.object]:

1 [...]在隐式更改活动对象时,由定义(6.1)和 new-expression (8.3.4)创建对象工会的成员(12.3),或创建临时对象时(7.4,15.2). [...]

1 [...] An object is created by a definition (6.1), by a new-expression (8.3.4), when implicitly changing the active member of a union (12.3), or when a temporary object is created (7.4, 15.2). [...]

由于您没有执行任何这些操作,因此不会创建任何对象.

Since you are not doing any of these things, no object is created.

此外,C ++不会隐式地将指向同一地址处不同对象的指针视为等效对象.您的&data[pos]计算指向char对象的指针.将其强制转换为T*不会使其指向该地址上的任何T对象,并且取消引用该指针具有未定义的行为. C ++ 17添加了 std::launder ,这是让编译器使用的一种方法知道您要访问的地址与指针所指向的对象不同.

Furthermore, C++ does not implicitly consider pointers to different object at the same address as equivalent. Your &data[pos] computes a pointer to a char object. Casting it to T* does not make it point to any T object residing at that address, and dereferencing that pointer has undefined behaviour. C++17 adds std::launder, which is a way to let the compiler know that you want to access a different object at that address than what you have a pointer to.

当您修改代码以使用位置newstd::launder并确保您没有未对齐的访问权限(为简洁起见,我假设您将其遗漏了)时,您的代码将具有定义的行为.

When you modify your code to use placement new and std::launder, and ensure you have no misaligned accesses (I presume you left that out for brevity), your code will have defined behaviour.

* 有关在将来的C ++版本中允许这样做的讨论.

* There is discussion on allowing this in a future version of C++.

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

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