严格的指针别名:针对特定问题的任何解决方案?

时间:2010-06-05 15:23:44

标签: c++ generics reinterpret-cast strict-aliasing type-punning

我遇到严重指针别名规则导致的问题。我有一个类型T来自模板和一些相同大小的整数类型Int(与sizeof一样)。我的代码基本上执行以下操作:

T x = some_other_t;
if (*reinterpret_cast <Int*> (&x) == 0)
  ...

因为T是一些可能有构造函数的任意(大小限制除外)类型,所以我不能建立TInt的联合。 (这仅在C ++ 0x中允许,GCC甚至不支持)。

有没有什么办法可以重写上面的伪代码以保留功能并避免破坏严格的别名规则?请注意,这是一个模板,我无法控制Tsome_other_t的值;赋值和后续比较确实发生在模板化代码中。

(对于记录,如果T包含任何位字段,则上述代码在GCC 4.5上开始中断。)

6 个答案:

答案 0 :(得分:1)

这个怎么样:

Int zero = 0;
T x = some_other_t;
if (std::memcmp(&x, &zero, sizeof(zero)) == 0)

它可能效率不高,但它应该摆脱警告。


ADDENDUM#1:

由于T被限制为与Int相同的大小,因此请将自己设为类型T的虚拟按位零值,并直接与其进行比较(而不是强制转换和比较agaist { {1}})。

如果你的程序是单线程的,你可以这样:

Int(0)

如果它是多线程的,您将希望避免使用静态成员template <typename T> class Container { public: void foo(T val) { if (zero_ == val) { // Do something } } private: struct Zero { Zero() {memset(&val, 0, sizeof(val));} bool operator==(const T& rhs) const {return val == rhs;} T val; }; static Zero zero_; }; ,并让每个容器实例保留它自己的zero_成员:

zero_

ADDENDUM#2:

让我以另一种更简单的方式提出上述补遗:

template <typename T>
class MTContainer
{
public:
    MTContainer() {memset(zero_, 0, sizeof(zero_));}

    void foo(T val)
    {
        if (val == zero_)
        {
            // Do something
        }
    }

private:
    T zero_;
};

答案 1 :(得分:1)

你听说过boost::optional吗?

我必须承认我不清楚这里的真正问题......但是boost :: optional允许按值存储但是知道实际内存是否已经初始化。我也允许建筑和破坏,所以我猜想这可能是合适的。

修改

我想我终于抓住了这个问题:你希望能够在内存中的各个点分配很多对象,并且你想知道此时的内存是否确实存在一个对象

不幸的是,你的解决方案有一个很大的问题:它是不正确的。如果T可以某种方式用null位模式表示,那么你会认为它是单位化的内存。

您必须自己添加至少一点信息。事实并非如此,毕竟这只是增长的3%(4字节为33位)。

例如,你可以使用一些mimick boost::optional但是以数组方式(以避免填充丢失)。

template <class T, size_t N>
class OptionalArray
{
public:


private:
  typedef unsigned char byte;

  byte mIndex[N/8+1];
  byte mData[sizeof(T)*N]; // note: alignment not considered
};

然后就这么简单:

template <class T, size_t N>
bool OptionalArray<T,N>::null(size_t const i) const
{
  return mIndex[i/8] & (1 << (i%8));
}

template <class T, size_t N>
T& OptionalArray<T,N>::operator[](size_t const i)
{
  assert(!this->null(i));
  return *reinterpret_cast<T*>(mData[sizeof(T)*i]);
}

注意:为简单起见,我没有考虑对齐问题。如果你不了解这个主题,请在摆弄记忆之前先阅读它:)

答案 2 :(得分:1)

static inline int is_T_0(const T *ob)
{
        int p;
        memcpy(&p, ob, sizeof(int));
        return p == 0;
}

void myfunc(void)
{
    T x = some_other_t;
    if (is_T_0(&x))
        ...

在我的系统上,GCC优化了is_T_0()memcpy(),只在myfunc()中生成了一些汇编指令。

答案 3 :(得分:1)

为什么不简单:

const Int zero = 0;
if (memcmp(&some_other_t, &zero, sizeof(zero)) == 0)
  /* some_other_t is 0 */

(您可能还想尝试将static限定符添加到zero以查看它是否在性能方面有所不同)

答案 4 :(得分:0)

使用33位计算机。 ;-P

答案 5 :(得分:-2)

感觉就像是黑客,但显然我找到了一个解决方案:使用volatile进行Int投射。基本上,我现在正在做的是:

T x = some_other_t;
if (*reinterpret_cast <volatile Int*> (&x) == 0)
  ...

bitfield T的问题现在已经消失。尽管如此,我对此并不感到高兴,因为volatile在C ++ AFAIK中没有明确定义......