In my code I just noticed that I quite often need to check for nullptr, even though nullptr should not be possible (according to specified requirements).
However, nullptr might still occur since other people might send a nullptr believing this is ok (unfortunately not everyone reads/writes specification), and this defect cannot be caught unless the problem is triggered in run-time during testing (and high test coverage is expensive). Thus it might lead to a lot of post-release bugs reported by customers.
e.g.
class data
{
virtual void foo() = 0;
};
class data_a : public data
{
public:
virtual void foo(){}
};
class data_b : public data
{
public:
virtual void foo(){}
};
void foo(const std::shared_ptr<data>& data)
{
if(data == nullptr) // good idea to check before use, performance and forgetting check might be a problem?
return;
data->foo();
}
Usually I would simply use value-types and pass by reference and copy. However, in some cases I need polymorphism which requires pointers or references.
So I have started to use the following "compile time polymorphism".
class data_a
{
public:
void foo(){}
private:
struct implementation;
std::shared_ptr<implementation> impl_; // pimpl-idiom, cheap shallow copy
};
class data_b
{
public:
void foo(){}
private:
struct implementation;
std::shared_ptr<implementation> impl_; // pimpl-idiom, cheap shallow copy
};
class data
{
public:
data(const data_a& x) : data_(x){} // implicit conversion
data(const data_b& x) : data_(x){} // implicit conversion
void foo()
{
boost::apply(foo_visitor(), data_);
}
private:
struct foo_visitor : public boost::static_visitor<void>
{
template<typename T>
void operator()(T& x){ x.foo(); }
};
boost::variant<data_a, data_b> data_;
}
void foo(const data& data)
{
data.foo();
}
Does anyone else think this is a good idea, when practical? Or am I missing something? Are there any potential problems with this practice?
EDIT:
The "problem" with using references is you cannot move ownership of a reference (e.g. returning an object).
data& create_data() { data_a temp; return temp; } // ouch... cannot return temp;
The problem with rvalue references (polymorphism does work with rvalue references?) then becomes that you cannot share ownership.
data&& create_data() { return std::move(my_data_); } // bye bye data
A "safe" pointer based on shared_ptr does sound like a good idea, but I would still like a solution where the non-nullness is enforced at compile time, maybe not possible.
You can always use the null object pattern and references only. You can't pass (well you sort of can but that is the user's error) a null reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With