将“this”指针转换为另一种类型不违反严格别名规则?

5

如果我这样做:

#include <ios>

using std::forward;

template<class T>
struct pod_version final{
    private:
        alignas(T) uint8_t m_data[sizeof(T)];
    public:
        pod_version()=default;
        pod_version(const pod_version&)=default;
        pod_version(pod_version&&)=default;
        ~pod_version()=default;
        pod_version& operator=(const pod_version&)=default;
        pod_version& operator=(pod_version&&)=default;

        template<class...V>void construct(V&&...v){
            new (m_data) T(forward<V>(v)...);
        }

        void destroy(){
            reinterpret_cast<T*>(m_data)->~T(); // g++: warning typed punned blah im allowed to break ur code LOL
            reinterpret_cast<T*>(this)->~T(); // g++: :D
        }
};

int main(){
    pod_version<int> x;
    x.construct(5);
    x.destroy();
    return 0;
}

注意:"m_data"和"this"应指向相同的位置...
gcc 4.8.1

1
我认为编译器并不够聪明,无法找出问题的原因。我不认为这是合法的。 - user529758
@Constructor 我的理解是std::forward和std::move都只是将一个rvalue转换为static_cast<T&&>(some_T)。而static_cast只是一种C风格的转换方式,所以这三种方式本质上都是C风格的转换方式。 - Mike
1
@Mike "而 static_cast 只是一种 C 风格的转换" -- 这部分不正确。 - user743382
@Mike 同样的问题(是的,-Wall已经开启)。 - jrok
@jrok 使用显式实例化来重现警告(template struct pod_version<int>;)。简单的字符串pod_version<int> p;并不会创建所有类方法。 - Constructor
显示剩余17条评论
3个回答

1
使用char而不是uint8_t
类型拼接规则对charsigned charunsigned char有一个特殊情况,并且仅限于这些类型。 uint8_t未必意味着它们之中的一个。
请注意,这个pod_version类的整个概念是可疑的。您正在强制使用不适用于这些类型的平凡复制语义。您的代码将尝试在未初始化的内存或对象的二进制图像上调用析构函数。 两者都将导致未定义的行为。

charsigned charunsigned char更改为未能消除警告。我相信uint8_tunsigned char的别名,即在cstdint中有 typedef unsigned char uint8_t - Mike
@Griwes:那不正确。标准明确允许“扩展的无符号整数类型”。请参见第3.9.1节,第2和第3段。 - Ben Voigt
@BenVoigt 好的,我的大脑不知怎么错过了那个。这仍然不是警告的理由,除非g++如此聪明,它会根据名称而不是类型发出警告(其中“聪明”部分是半挖苦的)。 - Griwes
事实仍然存在,使用任何变体的char仍会产生警告。 - Mike
@Mike:这可能是因为允许将数据类型转换为 char*,但反过来则不行。 - Ben Voigt

0
你的编译器在对进行强制类型转换时正确地报错,因为不是< char*>类型,而是< int8_t *>类型。严格的别名规则只允许将类型转换为< char* >、< unsigned char*>或< signed char*>,不允许其他类型。

可能编译器无法意识到将< pod_version* >转换为< T* >可能会导致一些问题。毕竟,这些是复杂的类型,编译器不知道您将要访问< T> 的哪个成员,它可能是一个< int8_t> 成员,在这种情况下,强制转换是完全可以的。毕竟,当您将相同的内存位置作为不同的基本类型进行访问时,才会触发未定义行为,而更改指针类型时不会。

无论如何,通过写入强制类型转换,您明确告诉编译器“我知道自己在做什么”,因此任何安全检查都是完全可选的。


特别是,编译器必须处理 struct X : pod_version<X> 的情况。 - Ben Voigt
@Mike:但是“共同的初始序列”规则仍然适用。 - Ben Voigt
@BenVoigt 是的,确实是这样...(我不知道CIS规则是什么:D) - Mike
@Mike:这个短语在C++标准(或最新的公共草案)中出现了4次,而且所有四次都是在规则陈述中(它被陈述了两次,不知道为什么会有冗余)。"如果一个标准布局联合包含多个共享公共初始序列的标准布局结构体,并且如果这个标准布局联合类型的对象包含其中一个标准布局结构体,则允许检查任何标准布局结构体成员的公共初始序列;" - Ben Voigt
但它不一定具有与T相同的初始序列。 - Mike
@cmaster "你的编译器正确地抱怨了m_data的转换,因为m_data不是char类型,而是int8_t类型。严格的别名规则禁止除char、unsigned char或signed char之外的任何类型的拼接。" int8_t很可能是char的typedef,这种情况下编译器看不出区别。此外,你只能将任何东西转换为char,而不能反过来。 - Chris_F

0

警告并非标准所必需。实现可以根据自己的判断随意发出警告,或者根本不发出。

值得一提的是,gcc 4.8.2使用-Wall选项编译您的代码时会静默通过。编辑:不,它不会。


你真的调用了 pod_version<T>::destroy 吗?(以确保该方法实际上被编译) - Mike
不,我没有。请发布一个自包含的代码,以重现问题(阅读提交指南获取更多信息)。 - n. m.
@n.m.:您的评论是在问题中的代码被修复后3分钟发表的。 - Ben Voigt
@BenVoigt 我不确定“fixed”是什么意思。截至目前(22:10 UTC),该代码无法编译,因为缺少#includeusing指令。 - n. m.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接