/*
This is an evolved solution based on an SO answer found
at http://stackoverflow.com/a/17159495/845568
1. I changed the name from `Data` to `Value` as it seems more logical in
this case.
2. It uses the same PIMPL approach as the original.
3. It uses `std::unqiue_ptr` instead of `new` and `delete` to manage object
lifetime
4. It uses an interface approach for the base class of the value container.
An implementation in the base class is unnecessary.
5. It uses a template class for the value container and includes a
specialization for `void`. This allows for a default constructor in `Value`.
*/
#include <iostream>
#include <memory>
// Interface for value objects
struct ValueImpl
{
virtual ~ValueImpl() {};
virtual std::unique_ptr<ValueImpl> clone() const = 0;
virtual void increments() = 0;
virtual void print() const = 0;
};
// Templated implementation of value class for easier use
template<typename T>
class ValueT : public ValueImpl
{
public:
ValueT(const T &k) : value_(k) {}
virtual std::unique_ptr<ValueImpl> clone() const
{
return std::unique_ptr<ValueImpl>(new ValueT(value_));
}
void increments()
{
value_++;
}
void print() const
{
std::cout << typeid(T).name() << "(" << value_ << ") " << std::endl;
}
private:
T value_;
};
// Specialization of value class to provide a default ctor in Value
template<>
class ValueT<void> : public ValueImpl
{
public:
ValueT() {}
virtual std::unique_ptr<ValueImpl> clone() const
{
return std::unique_ptr<ValueImpl>(new ValueT());
}
void increments() { }
void print() const
{
std::cout << "(void)" << std::endl;
}
};
// Value container using PIMPL idiom.
class Value
{
public:
Value() : value_(new ValueT<void>) {}
Value(const Value & other) : value_(other.value_->clone()) {}
// Construct a Value given an actual implementation:
// This allocates memory on the heap, hidden in clone()
explicit Value(const ValueImpl& value) : value_(value.clone()) {}
// Destruct the data: must deallocate the memory
virtual ~Value() {}
// Copy constructor and assignment operator:
// Implements a value semantics by allocating new memory
Value & operator=(const Value & other)
{
if(&other != this)
{
value_ = std::move(other.value_->clone());
}
return *this;
}
// Custom "polymorphic" methods
void increments() { value_->increments(); }
void print() { value_->print(); }
private:
std::unique_ptr<ValueImpl> value_;
};
int main()
{
// use typedefs if you like. they are really not necessary for this example
Value iv(ValueT<int>(1));
Value dv(ValueT<double>(1.2));
Value value;
value.print();
value = dv;
value.print();
value = iv;
value.print();
value.increments();
value.print();
}