Simple storage class in C++ and strict aliasing
I have the following code for having a small class for storage.
#include <iostream>
template<typename T>
class storage
{
private:
struct destroy
{
T& m_t;
destroy(T& t) : m_t(t) { }
~destroy() { m_t.~T(); }
};
char m_c[sizeof(T)];
void* address() { return &m_c[0]; }
public:
void set(const T& t) { new (address()) T(t); }
T get()
{
T& t = *static_cast<T*>(address());
destroy _d(t);
return t;
}
};
template<typename T>
class choosable_storage
{
private:
union
{
T* m_p;
storage<T> m_storage;
};
bool m_direct;
public:
choosable_storage() : m_direct(false) { }
void set_direct(const T& t)
{
m_direct = true;
m_storage.set(t);
}
void set_indirect(T* const t) { m_p = t; }
T get()
{
if (m_direct) return m_storage.get();
return *m_p;
}
};
int main(void)
{
storage<int> s; // no problem开发者_高级运维s
s.set(42);
std::cout << s.get() << std::endl;
int i = 10;
choosable_storage<int> c1; // strict aliasing warnings
c1.set_indirect(&i);
std::cout << c1.get() << std::endl;
choosable_storage<int> c2;
c2.set_direct(i);
std::cout << c2.get() << std::endl;
return 0;
}
gcc 4.4 warns that I break the strict aliasing rules in storage::get()
when I return.
AFAIK, I do not violate any rules. Do I actually violate strict aliasing or is gcc getting picky here?
And is there a way of having it warning free without disabling strict aliasing?
Thanks
EDIT:
On the other hand, the following implementation does not give any warnings:
template<typename T>
class storage
{
private:
struct destroy
{
T& m_t;
destroy(T& t) : m_t(t) { }
~destroy() { m_t.~T(); }
T const& operator()() const { return m_t; }
};
char m_c[sizeof(T)];
public:
void set(const T& t) { new(static_cast<void*>(m_c)) T(t); }
T get(void) { return destroy(*static_cast<T*>(static_cast<void*>(m_c)))(); }
};
EDIT:
gcc 4.5 and up does not issue a warning - so apparently this was just a misinterpretation of the strict aliasing rules or a bug in gcc 4.4.x
Do I actually violate strict aliasing or is gcc getting picky here?
There are two different interpretation of the strict aliasing rule:
- the usual weak strict aliasing rule: (except for
char
/unsigned char
type) you cannot use a cast or union to perform type punning, you needmemcpy
(or twovolatile
accesses); such code really is not very reasonable, and is explicitly forbidden in C (except the latest, very absurd, C standard) and C++. - the unusual strong strict aliasing rule: you cannot reuse memory: once a region of memory has a "dynamic type" (a what?), it cannot be used with a different type. So you just cannot write an allocator function (
malloc
alternative) C, unless it only callsmalloc
/free
each time.
The strong rule is ill-defined, break lots of reasonable code, and for some reason was chosen by the GCC maintainers (based entirely on self delusion and circular justifications - this is really ugly). To make C++ code work with the strong strict aliasing optimisation, the maintainers of G++ added pessimisations for typical C++ code (based on more self delusion), to keep the optimisation!
I do not know if/when they realise their mistake, in case of doubt just disable strict aliasing. It is a very minor optimisation anyway.
For the purposes of this question, strict aliasing rule states, essentially, that you are not supposed to access an object except through a pointer/reference of its own type or a pointer/reference to a character type (char or unsigned char).
In your code, you have an array m_c of elements of type char, and you're trying to access it through a reference of type T. That's a strict aliasing violation. On some more exotic platforms, this could have repercussions, for example, if m_c is not properly aligned to hold the element of type T.
精彩评论