After writing memset( &x, 0, sizeof( x ) ); for the millionth time,
you might start to get lazy and decide it's a good idea to #define
ZERO( p ) memset( p, 0, sizeof( *p ) );. This turns out to be very easy
to misuse:

int x;
ZERO( &x ); // cool
int y[ 8 ];
ZERO( &y ); // cool
ZERO( y ); // y[ 0 ] = 0, no warnings
int * z = y;
ZERO( z ); // y[ 0 ] = 0
ZERO( &z ); // z = NULL

You can try things like making ZERO take a pointer instead, but you
still always end up with cases where the compiler won't tell you that
you screwed up. The problem is that there's no way for ZERO to do the
right thing to a pointer because it can't know how big the object being
pointed at is. The simplest solution is to simply not allow that:

template< typename T > struct IsAPointer { enum { value = false }; };
template< typename T > struct IsAPointer< T * > { enum { value = true }; };
template< typename T >
void zero( T & x ) {
static_assert( !IsAPointer< T >::value );
memset( &x, 0, sizeof( x ) );
}

and as a bonus, we can use the same trick from last time to make it work
on fixed-size arrays too:

template< typename T, size_t N >
void zero( T x[ N ] ) {
memset( x, 0, sizeof( T ) * N );
}