IMHO the rule is because the source pointers are dangerous, because liability for property and destruction becomes unclear. Consequently, multiple encapsulations around the concept ( smart_ptr , auto_ptr , unique_ptr , ...). First consider using such encapsulations instead of the raw pointer in your container.
Secondly, why do you need to specify pointers in the container? I mean, they must contain complete objects; they have a allocator as a template argument for the exact allocation of memory in the end. In most cases, you need pointers because you have an OO approach that makes great use of polymorphism. You should reconsider this approach. For example, you can replace:
struct Animal {virtual std::string operator()() = 0;}; struct Dog : Animal {std::string operator()() {return "woof";}}; struct Cat : Animal {std::string operator()() {return "miaow";}};
Something like this using Boost.Variant :
struct Dog {std::string operator()() {return "woof";}}; struct Cat {std::string operator()() {return "miaow";}}; typedef boost::variant<Dog, Cat> Animal;
That way, when you add a new animal, you donβt inherit anything, you just add it to this option.
You can also consider a bit more complex, but more general, using Boost.Fusion :
struct Dog {std::string talk; Dog() : talk("wook"){}}; struct Cat {std::string talk; Cat() : talk("miaow"){}}; BOOST_FUSION_ADAPT_STRUCT(Dog, (std::string, talk)) BOOST_FUSION_ADAPT_STRUCT(Cat, (std::string, talk)) typedef boost::fusion::vector<std::string> Animal; int main() { vector<Animal> animals; animals.push_back(Dog()); animals.push_back(Cat()); using boost::fusion::at; using boost::mpl::int_; for(auto a : animals) { cout << at<int_<0>>(a) << endl; } }
Thus, you do not even change the aggregate version and algorithms on animals, you just need to provide FUSION_ADAPT, corresponding to the premises of the algorithms used. Both versions (variant and merge) allow you to define groups of orthogonal objects, a useful thing that you cannot do with inheritance trees.