Строгий указатель псевдонимов: какое-либо решение для конкретной проблемы? - PullRequest
8 голосов
/ 05 июня 2010

У меня проблема, связанная с нарушением строгого правила наложения указателей. У меня есть тип T, который исходит из шаблона, и некоторый целочисленный тип Int того же размера (как с sizeof). Мой код по существу делает следующее:

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

Поскольку T - это некоторый произвольный (кроме ограничения размера) тип, который может иметь конструктор, я не могу создать объединение T и Int. (Это разрешено только в C ++ 0x и даже не поддерживается GCC).

Можно ли как-нибудь переписать указанный выше псевдокод, чтобы сохранить функциональность и избежать нарушения строгого правила псевдонимов? Обратите внимание, что это шаблон, я не могу контролировать T или значение some_other_t; присваивание и последующее сравнение происходят внутри шаблонного кода.

(Для записи вышеупомянутый код начал ломаться на GCC 4.5, если T содержит какие-либо битовые поля.)

Ответы [ 6 ]

1 голос
/ 15 июля 2012

Почему бы просто:

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

(вы также можете попытаться добавить квалификатор static к zero, чтобы увидеть, влияет ли он на производительность)

1 голос
/ 06 июня 2010
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().

1 голос
/ 05 июня 2010

Вы слышали о boost::optional?

Должен признать, что у меня нет ясности относительно реальной проблемы здесь ... но boost :: option позволяет хранить по значению и все же знать, была ли инициализирована фактическая память. Я также допускаю строительство и разрушение на месте, так что я думаю, что это может подойти.

EDIT

Мне кажется, я наконец-то понял проблему: вы хотите иметь возможность размещать много объектов в разных точках памяти, и вы хотите знать, действительно ли память в этой точке содержит объект или нет .

К сожалению, у вашего решения есть огромная проблема: оно неверно. Если когда-либо T может быть каким-либо образом представлен в виде null битовой комбинации, тогда вы будете думать, что это унифицированная память.

Вам придется прибегнуть к помощи, чтобы добавить хотя бы один бит информации. На самом деле это немного, ведь это всего лишь 3% прироста (33 бита на 4 байта).

Вы можете, например, использовать какой-нибудь 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]);
}

note : Для простоты я не рассматривал вопрос выравнивания. Если вы не знаете о предмете, прочитайте об этом, прежде чем возиться с памятью:)

1 голос
/ 05 июня 2010

Как насчет этого:

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

Возможно, это не так эффективно, но нужно избавиться от предупреждения.


ADDENDUM # 1:

Поскольку T ограничен размером, равным Int, создайте фиктивное битовое нулевое значение типа T и сравните его непосредственно с ним (вместо приведения и сравнения agaist 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_ член:

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

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

private:
    T zero_;
};

ADDENDUM # 2:

Позвольте мне добавить вышеприведенное дополнение другим, более простым способом:

// zero is a member variable and is inialized in the container's constructor
T zero;
std::memset(&zero, 0, sizeof(zero));

T x = some_other_t;
if (x == zero)
0 голосов
/ 05 июня 2010

Это похоже на взлом, но, видимо, я нашел решение: использовать volatile для Int кастинга. По сути, то, что я делаю сейчас:

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

Проблема с битовым полем T исчезла. Тем не менее, я не очень доволен этим, так как volatile недостаточно определен в C ++ AFAIK ...

0 голосов
/ 05 июня 2010

Используйте 33-битный компьютер.; -Р

...