Строгие счетные указатели: любое решение для конкретной проблемы?

StackOverflow https://stackoverflow.com/questions/2980920

Вопрос

У меня есть проблема, вызванная нарушением правила псевдонивания строгого указателя. У меня есть тип 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 содержит любые битовые поля.)

Это было полезно?

Решение

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().

Другие советы

Ты слышал о boost::optional ?

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

РЕДАКТИРОВАТЬ:

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

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

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

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

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

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

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

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


Приложение № 1:

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

Приложение № 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)

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

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

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

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

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

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

Проблема с Битфилдом T теперь ушел. Тем не менее, я не очень доволен этим, как volatile не четко определен в C ++ afaik ...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top