C++ Template Trick: Detecting Object Slicing

Object slicing often happens when you pass the object by value. Compiler will do implicitly conversion from derived to base for you without any warning message.

If you want to detect object slicing, you're on your own. However, template can help you.

Because object slicing will call copy constructor of base class, what you can do is to "hijack" it. The magic looks like:

#include <type_traits>
#include "boost\static_assert.hpp"

template<bool>
struct SliceHelper
{
};
template<>
struct SliceHelper<false>
{
    typedef void type;
};
#define DETECTSLICE(NAME,SIZE)\
    enum {_SizeOfClass=SIZE};\
    void _SizeValidation() {BOOST_STATIC_ASSERT(sizeof(NAME)==_SizeOfClass);}\
    template <typename T> NAME(const T &,typename SliceHelper<sizeof(T)==_SizeOfClass || !std::tr1::is_base_of<NAME,T>::value>::type * = 0)\
    {\
        typedef typename T::sliced type;\
    }
struct A
{
    int a;
    A() {}
    DETECTSLICE(A,4)
};
struct B:public A
{
};
struct C:public A
{
    int b;
};
struct D
{
    int a;
};
struct E
{
    int a,b;
};
void f(A) {}
int main()
{
    B b;
    C c;
    D d;
    E e;
    A a;
    A a0(a);
    A a1(b);
    //A a2(c); //error
    //A a3(d); //error
    //A a4(e); //error
    f(a);
    f(b);
    //f(c); //error
    //f(d); //error
    //f(e); //error
}

Notice:

1. The template constructor is not a copy constructor. According to the standard, copy constructor should be non-template. But the template constructor can still be chosen to copy construct the object :-)

2. You can not use sizeof in the member function declaration because the class is incomplete at that point. You can only use sizeof inside the member function definition. That is why we have to specify the size of the class explicitly.

Comments