Строгие счетные указатели: любое решение для конкретной проблемы?
-
24-10-2019 - |
Вопрос
У меня есть проблема, вызванная нарушением правила псевдонивания строгого указателя. У меня есть тип 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 ...