【问题标题】:Casting the "this" pointer to another type does not violate strict aliasing?将“this”指针转换为另一种类型不会违反严格的别名?
【发布时间】:2014-01-11 09:06:34
【问题描述】:

所以如果我这样做:

#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

【问题讨论】:

  • 我想说编译器不够聪明,无法弄清楚(出于某种原因)。我认为这确实不合法。
  • 为什么要使用 C 风格的类型转换?
  • @Constructor 与函数样式转换或 std::static_cast 或?你在说什么?
  • @Mike 我说的是(V&amp;&amp;)v...(T*)m_data
  • @Mike "and static_cast 只是一个 c 风格的演员" -- 那部分是不对的。

标签: c++ templates pointers c++11 strict-aliasing


【解决方案1】:

使用char,而不是uint8_t

类型双关语规则对charsigned charunsigned char 有特殊情况,并且仅限于这些类型。 uint8_t 不需要表示其中之一。

请注意,这个pod_version 类的整个概念是可疑的。您正在对无效的类型强制执行简单的复制语义。您的代码将尝试在未初始化的内存或对象的二进制图像上调用析构函数。两者都会导致未定义的行为。

【讨论】:

  • 更改为 char signed char unsigned char 并没有消除警告。我相信uint8_tunsigned char 的别名,即typedef unsigned char uint8_t 在cstdint 的某个地方
  • @Griwes:这不正确。标准明确允许“扩展无符号整数类型”。请参阅第 3.9.1 节第 2 和第 3 段。
  • @BenVoigt 好吧,我的大脑不知何故错过了这一点。这仍然不是警告的原因,除非 g++ 非常聪明,它根据名称而不是它命名的类型发出警告(其中“聪明”会部分讽刺)。
  • 事实上,使用char 的任何变体仍然会产生警告。
  • @Mike:这可能是因为将 转换为 char* 是允许的,反之则不行。
【解决方案2】:

您的编译器正确地抱怨m_data 的转换,因为m_data 不是char* 类型,而是int8_t* 类型。严格的别名规则不允许在 char*unsigned char*signed char* 以外的任何地方进行类型双关。

很可能,编译器无法通过将pod_version&lt;T&gt;* 转换为T* 来判断出某种邪恶的含义。毕竟,这些都是复杂类型,编译器不知道您要访问T 的哪些成员,它可能是int8_t 成员,在这种情况下,强制转换是完全可以的。毕竟,未定义的行为仅在您以不同的基本类型访问相同的内存位置时调用,而不是在您更改指针的类型时调用。

在任何情况下,您都可以通过编写强制转换明确地告诉编译器“我知道自己在做什么”,因此任何健全性检查都是可选的。

【讨论】:

  • 特别是编译器要处理struct X : pod_version&lt;X&gt;的情况
  • @Mike:但“共同初始序列”规则确实仍然适用。
  • @BenVoigt mhm 是的,确实...(我不知道 CIS 规则是什么 D:)
  • @Mike:这个短语在 C++ 标准(或最新的公共草案)中出现了 4 次,并且所有 4 次都在规则声明中(它被声明了两次,不知道为什么会出现冗余)“如果一个标准布局联合包含几个共享一个公共初始序列的标准布局结构,并且如果这个标准布局联合类型的对象包含一个标准布局结构,则允许检查任何一个的公共初始序列标准布局结构成员;"
  • 但不一定和T有相同的初始序列
【解决方案3】:

标准不要求警告。实现可能会以任何他们认为合适的原因警告您,或者根本不会警告您。

不管怎样,gcc 4.8.2 使用 -Wall 静默编译您的代码。 编辑:不,它没有。

【讨论】:

  • 你实际上打电话给pod_version&lt;T&gt;::destroy? (以确保该方法实际上正在编译)
  • 不,我没有。请发布能够重现问题的独立代码(阅读提交指南了解更多信息)。
  • @n.m.:您的评论是在问题中的代码修复 3 分钟后发表的。
  • @BenVoigt 我不确定“固定”是什么意思。该代码不可编译,因为截至目前(22:10 UTC),它缺少 #includeusing 指令。
猜你喜欢
  • 2017-05-08
  • 2010-12-27
  • 1970-01-01
  • 2020-01-09
  • 2020-12-14
  • 2010-10-08
相关资源
最近更新 更多