How to derive std::vector? - c++11

I want a class derived from std::vector for my operator []
template<class T>
class MyVector : public std::vector<T>
{
public:
// ?...
const T &operator[](size_t index) const
{
//...
}
T &operator[](size_t index)
{
//...
}
};
int main()
{
MyVector<int> myVec = { 1, 2, 3 };
//...
}
How can I do this deriving all std::vector constructors and assigning operators for C++11?

Usually this is a bad idea.
First, because if someone was as silly as to new MyVector<int> and then store that in a std::vector<int> and then delete through that pointer, you have UB. But that is a pretty stupid use case; using new on std::vector is really bad code smell.
Second, because it seems pointless and confusing.
But you can do it.
template<class T>
class MyVector : public std::vector<T>
{
public:
using std::vector<T>::vector;
using std::vector<T>::operator=;
MyVector(MyVector const&)=default;
MyVector(MyVector &&)=default;
MyVector& operator=(MyVector const&)=default;
MyVector& operator=(MyVector &&)=default;
const T &operator[](size_t index) const
{
//...
}
T &operator[](size_t index)
{
//...
}
};
now, this doesn't support construct-from std::vector<T>.
MyVector( std::vector<T>&& o ):std::vector<T>(std::move(o)) {}
MyVector( std::vector<T> const& o ):std::vector<T>(o) {}
MyVector& operator=( std::vector<T>&& o ) {
static_cast<std::vector<T&>>(*this) = std::move(o);
return *this;
}
MyVector& operator=( std::vector<T> const& o ) {
static_cast<std::vector<T&>>(*this) = o;
return *this;
}
that covers some last cases.
This won't be completely transparent, but it covers 99.9% of cases.

Related

A container that accumulates its elements metrics

I'm looking into a solution of building containers which track stored size of their elements in addition to basic functions.
So far I didn't saw a solution which doesn't create a huge amount of boilerplate code of each invalidating member of container. This also assumes that stored elements cannot change size after being stored.
Unless standard containers have some feature that allows to inject such behaviour. The following example should be working one, albeit abridged for brevity. The declarations used are:
typedef uint8_t Byte;
typedef Byte PacketId;
template <class T>
struct CollectionTraits {
typedef T collection_type;
typedef typename collection_type::value_type value_type;
typedef typename collection_type::size_type size_type;
typedef typename collection_type::iterator iterator;
typedef typename collection_type::reference reference;
typedef typename collection_type::const_iterator const_iterator;
const_iterator begin() const { return _collection.begin(); }
const_iterator end() const { return _collection.end(); }
iterator begin() { return _collection.begin(); }
iterator end() { return _collection.end(); }
size_type size() const { return _collection.size(); }
protected:
T _collection;
};
struct Packet : CollectionTraits<std::vector<Byte>>
{
PacketId id;
};
The container itself:
struct PacketList : CollectionTraits<std::deque<Packet>>
{
public:
typedef Packet::size_type data_size;
void clear() { _collection.clear(); _total_size = 0; }
data_size total_size() const { return _total_size; }
void push_back(const Packet& v) {
_collection.push_back(v);
_add(v);
}
void push_back(const Packet&& v) {
_collection.push_back(std::move(v));
_add(v);
}
void push_front(const Packet& v) {
_collection.push_front(v);
_add(v);
}
void push_front(const Packet&& v) {
_collection.push_front(std::move(v));
_add(v);
}
void pop_back() {
_remove(_collection.back());
_collection.pop_back();
}
void erase(const_iterator first, const_iterator last) {
for(auto it = first; it != last; ++it) _remove(*it);
_collection.erase(first, last);
}
PacketList() : _total_size(0) {}
PacketList(const PacketList& other) : _total_size(other._total_size) {}
private:
void _add(const Packet& v) { _total_size += v.size(); }
void _remove(const Packet& v) { _total_size -= v.size(); }
data_size _total_size;
};
The interface in result should similar to a standard container. Is there a way to avoid this amount of repeated code? Is there some standard solution for this problem?

using decltype w/ parameter pack for C++11

I'm trying to get the multi_index_t code from the second answer here answered by davidhigh to work with C++11. C++11 does not support auto& type returns.
I converted the return types for the class, but I don't understand how/if it's possible to support the helper function multi_index() without using C++14.
The code:
#include<array>
template<int dim>
struct multi_index_t
{
std::array<int, dim> size_array;
template<typename ... Args>
multi_index_t(Args&& ... args) : size_array(std::forward<Args>(args) ...) {}
struct iterator
{
struct sentinel_t {};
std::array<int, dim> index_array = {};
std::array<int, dim> const& size_array;
bool _end = false;
iterator(std::array<int, dim> const& size_array) : size_array(size_array) {}
iterator& operator++()
{
for (int i = 0;i < dim;++i)
{
if (index_array[i] < size_array[i] - 1)
{
++index_array[i];
for (int j = 0;j < i;++j) { index_array[j] = 0; }
return *this;
}
}
_end = true;
return *this;
}
std::array<int, dim>& operator*() { return index_array; }
bool operator!=(sentinel_t) const { return !_end; }
};
iterator begin() const { return iterator{ size_array }; }
iterator end() const { return typename iterator::sentinel_t{}; }
};
template<typename ... index_t>
auto multi_index(index_t&& ... index) // <-- this doesn't compile
{
static constexpr int size = sizeof ... (index_t);
auto ar = std::array<int, size>{std::forward<index_t>(index) ...};
return multi_index_t<size>(ar);
}
According to this answer, you can't recursively expand the variadic function template via decltype(). Any ideas?
C++11 does not support auto& type returns.
So you can simply explicit the types.
For multi_index() you have that return a multi_index_t<size>, where size is sizeof...(index_t), so you can write
template<typename ... index_t>
multi_index_t<sizeof...(index_t)> multi_index(index_t&& ... index)
According to this answer, you can't recursively expand the variadic function template via decltype.
Correct, but I don't see recursion in your multi_index() function, so I don't see how apply recursion over decltype().
If you really want (but why?), you can explicit the returning type through decltype() as follows
template<typename ... index_t>
auto multi_index(index_t&& ... index)
-> decltype( multi_index_t<sizeof...(index_t)>
{ std::array<int, sizeof...(index_t)>
{{ std::forward<index_t>(index) ... }} } )
but I don't see a reason to do this instead of simply explicit multi_index_t<sizeof...(index_t)>

Persistent expression templates with unique_ptr and matrices

I want to use expression templates to create a tree of objects that persists across statement. Building the tree initially involves some computations with the Eigen linear algebra library. The persistent expression template will have additional methods to compute other quantities by traversing the tree in different ways (but I'm not there yet).
To avoid problems with temporaries going out of scope, subexpression objects are managed through std::unique_ptr. As the expression tree is built, the pointers should be propagated upwards so that holding the pointer for the root object ensures all objects are kept alive. The situation is complicated by the fact that Eigen creates expression templates holding references to temporaries that go out of scope at the end of the statement, so all Eigen expressions must be evaluated while the tree is being constructed.
Below is a scaled-down implementation that seems to work when the val type is an object holding an integer, but with the Matrix type it crashes while constructing the output_xpr object. The reason for the crash seems to be that Eigen's matrix product expression template (Eigen::GeneralProduct) gets corrupted before it is used. However, none of the destructors either of my own expression objects or of GeneralProduct seems to get called before the crash happens, and valgrind doesn't detect any invalid memory accesses.
Any help will be much appreciated! I'd also appreciate comments on my use of move constructors together with static inheritance, maybe the problem is there somewhere.
#include <iostream>
#include <memory>
#include <Eigen/Core>
typedef Eigen::MatrixXi val;
// expression_ptr and derived_ptr: contain unique pointers
// to the actual expression objects
template<class Derived>
struct expression_ptr {
Derived &&transfer_cast() && {
return std::move(static_cast<Derived &&>(*this));
}
};
template<class A>
struct derived_ptr : public expression_ptr<derived_ptr<A>> {
derived_ptr(std::unique_ptr<A> &&p) : ptr_(std::move(p)) {}
derived_ptr(derived_ptr<A> &&o) : ptr_(std::move(o.ptr_)) {}
auto operator()() const {
return (*ptr_)();
}
private:
std::unique_ptr<A> ptr_;
};
// value_xpr, product_xpr and output_xpr: expression templates
// doing the actual work
template<class A>
struct value_xpr {
value_xpr(const A &v) : value_(v) {}
const A &operator()() const {
return value_;
}
private:
const A &value_;
};
template<class A,class B>
struct product_xpr {
product_xpr(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) :
a_(std::move(a).transfer_cast()), b_(std::move(b).transfer_cast()) {
}
auto operator()() const {
return a_() * b_();
}
private:
derived_ptr<A> a_;
derived_ptr<B> b_;
};
// Top-level expression with a matrix to hold the completely
// evaluated output of the Eigen calculations
template<class A>
struct output_xpr {
output_xpr(expression_ptr<derived_ptr<A>> &&a) :
a_(std::move(a).transfer_cast()), result_(a_()) {}
const val &operator()() const {
return result_;
}
private:
derived_ptr<A> a_;
val result_;
};
// helper functions to create the expressions
template<class A>
derived_ptr<value_xpr<A>> input(const A &a) {
return derived_ptr<value_xpr<A>>(std::make_unique<value_xpr<A>>(a));
}
template<class A,class B>
derived_ptr<product_xpr<A,B>> operator*(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) {
return derived_ptr<product_xpr<A,B>>(std::make_unique<product_xpr<A,B>>(std::move(a).transfer_cast(), std::move(b).transfer_cast()));
}
template<class A>
derived_ptr<output_xpr<A>> eval(expression_ptr<derived_ptr<A>> &&a) {
return derived_ptr<output_xpr<A>>(std::make_unique<output_xpr<A>>(std::move(a).transfer_cast()));
}
int main() {
Eigen::MatrixXi mat(2, 2);
mat << 1, 1, 0, 1;
val one(mat), two(mat);
auto xpr = eval(input(one) * input(two));
std::cout << xpr() << std::endl;
return 0;
}
Your problem appears to be that you are using someone else's expression templates, and storing the result in an auto.
(This happens in product_xpr<A>::operator(), where you call *, which if I read it right, is an Eigen multiplication that uses expression templates).
Expression templates are often designed to presume the entire expression will occur on a single line, and it will end with a sink type (like a matrix) that causes the expression template to be evaluated.
In your case, you have a*b expression template, which is then used to construct an expression template return value, which you later evaluate. The lifetime of temporaries passed to * in a*b are going to be over by the time you reach the sink type (matrix), which violates what the expression templates expect.
I am struggling to come up with a solution to ensure that all temporary objects have their lifetime extended. One thought I had was some kind of continuation passing style, where instead of calling:
Matrix m = (a*b);
you do
auto x = { do (a*b) pass that to (cast to matrix) }
replace
auto operator()() const {
return a_() * b_();
}
with
template<class F>
auto operator()(F&& f) const {
return std::forward<F>(f)(a_() * b_());
}
where the "next step' is passed to each sub-expression. This gets trickier with binary expressions, in that you have to ensure that the evaluation of the first expression calls code that causes the second sub expression to be evaluated, and then the two expressions are combined, all in the same long recursive call stack.
I am not proficient enough in continuation passing style to untangle this knot completely, but it is somewhat popular in the functional programming world.
Another approach would be to flatten your tree into a tuple of optionals, then construct each optional in the tree using a fancy operator(), and manually hook up the arguments that way. Basically do manual memory management of the intermediate values. This will work if the Eigen expression templates are either move-aware or do not have any self-pointers, so that moving at the point of construction doesn't break things. Writing that would be challenging.
Continuation passing style, suggested by Yakk, solves the problem and isn't too insane (not more insane than template metaprogramming in general anyhow). The double lambda evaluation for the arguments of binary expressions can be tucked away in a helper function, see binary_cont in the code below. For reference, and since it's not entirely trivial, I'm posting the fixed code here.
If somebody understands why I had to put a const qualifier on the F type in binary_cont, please let me know.
#include <iostream>
#include <memory>
#include <Eigen/Core>
typedef Eigen::MatrixXi val;
// expression_ptr and derived_ptr: contain unique pointers
// to the actual expression objects
template<class Derived>
struct expression_ptr {
Derived &&transfer_cast() && {
return std::move(static_cast<Derived &&>(*this));
}
};
template<class A>
struct derived_ptr : public expression_ptr<derived_ptr<A>> {
derived_ptr(std::unique_ptr<A> &&p) : ptr_(std::move(p)) {}
derived_ptr(derived_ptr<A> &&o) = default;
auto operator()() const {
return (*ptr_)();
}
template<class F>
auto operator()(F &&f) const {
return (*ptr_)(std::forward<F>(f));
}
private:
std::unique_ptr<A> ptr_;
};
template<class A,class B,class F>
auto binary_cont(const derived_ptr<A> &a_, const derived_ptr<B> &b_, const F &&f) {
return a_([&b_, f = std::forward<const F>(f)] (auto &&a) {
return b_([a = std::forward<decltype(a)>(a), f = std::forward<const F>(f)] (auto &&b) {
return std::forward<const F>(f)(std::forward<decltype(a)>(a), std::forward<decltype(b)>(b));
});
});
}
// value_xpr, product_xpr and output_xpr: expression templates
// doing the actual work
template<class A>
struct value_xpr {
value_xpr(const A &v) : value_(v) {}
template<class F>
auto operator()(F &&f) const {
return std::forward<F>(f)(value_);
}
private:
const A &value_;
};
template<class A,class B>
struct product_xpr {
product_xpr(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) :
a_(std::move(a).transfer_cast()), b_(std::move(b).transfer_cast()) {
}
template<class F>
auto operator()(F &&f) const {
return binary_cont(a_, b_,
[f = std::forward<F>(f)] (auto &&a, auto &&b) {
return f(std::forward<decltype(a)>(a) * std::forward<decltype(b)>(b));
});
}
private:
derived_ptr<A> a_;
derived_ptr<B> b_;
};
template<class A>
struct output_xpr {
output_xpr(expression_ptr<derived_ptr<A>> &&a) :
a_(std::move(a).transfer_cast()) {
a_([this] (auto &&x) { this->result_ = x; });
}
const val &operator()() const {
return result_;
}
private:
derived_ptr<A> a_;
val result_;
};
// helper functions to create the expressions
template<class A>
derived_ptr<value_xpr<A>> input(const A &a) {
return derived_ptr<value_xpr<A>>(std::make_unique<value_xpr<A>>(a));
}
template<class A,class B>
derived_ptr<product_xpr<A,B>> operator*(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) {
return derived_ptr<product_xpr<A,B>>(std::make_unique<product_xpr<A,B>>(std::move(a).transfer_cast(), std::move(b).transfer_cast()));
}
template<class A>
derived_ptr<output_xpr<A>> eval(expression_ptr<derived_ptr<A>> &&a) {
return derived_ptr<output_xpr<A>>(std::make_unique<output_xpr<A>>(std::move(a).transfer_cast()));
}
int main() {
Eigen::MatrixXi mat(2, 2);
mat << 1, 1, 0, 1;
val one(mat), two(mat), three(mat);
auto xpr = eval(input(one) * input(two) * input(one) * input(two));
std::cout << xpr() << std::endl;
return 0;
}

Get template to work well with unique_ptr to interfaces in C++

First of all, there's no such built in concept as "interface". By interface in C++, I really mean some abstract base class that looks like:
struct ITreeNode
{
... // some pure virtual functions
};
Then we can have concrete structs that implement the interface, such as:
struct BinaryTreeNode : public ITreeNode
{
BinaryTreeNode* LeftChild;
BinaryTreeNode* RightChild;
// plus the overriden functions
};
It makes good sense: ITreeNode is an interface; not every implementation has Left & Right children - only BinaryTreeNode does.
To make things widely reusable, I want to write a template. So the ITreeNode needs to be ITreeNode<T>, and BinaryTreeNode needs to be BinaryTreeNode<T>, like this:
template<typename T>
struct BinaryTreeNode : public ITreeNode<T>
{
};
To make things even better, let's use unique pointer(smart point is more common, but I know the solution - dynamic_pointer_cast).
template<typename T>
struct BinaryTreeNode : public ITreeNode<T>
{
typedef std::shared_ptr<BinaryTreeNode<T>> SharedPtr;
typedef std::unique_ptr<BinaryTreeNode<T>> UniquePtr;
// ... other stuff
};
Likewise,
template<typename T>
struct ITreeNode
{
typedef std::shared_ptr<ITreeNode<T>> SharedPtr;
typedef std::unique_ptr<ITreeNode<T>> UniquePtr;
};
It's all good, until this point:
Let's assume now we need to write a class BinaryTree.
There's a function insert that takes a value T and insert it into the root node using some algorithm(naturally it will be recursive).
In order to make the function testable, mockable and follow good practice, the arguments need to be interface, rather than concrete classes. (Let's say this is a rigid rule that cannot be broken.)
template<typename T>
void BinaryTree<T>::Insert(const T& value, typename ITreeNode<T>::UniquePtr& ptr)
{
Insert(value, ptr->Left); // Boooooom, exploded
// ...
}
Here's the problem:
Left is not a field of ITreeNode! And worst of all, you cannot cast a unique_ptr<Base> to unique_ptr<Derived>!
What's the best practice for a scenario like this?
Thanks a lot!
Ok, over-engineering it is! But note that, for the most part, such low level data structures benefit HUGELY from transparency and simple memory layouts. Placing the level of abstraction above the container can give significant performance boosts.
template<class T>
struct ITreeNode {
virtual void insert( T const & ) = 0;
virtual void insert( T && ) = 0;
virtual T const* get() const = 0;
virtual T * get() = 0;
// etc
virtual ~ITreeNode() {}
};
template<class T>
struct IBinaryTreeNode : ITreeNode<T> {
virtual IBinaryTreeNode<T> const* left() const = 0;
virtual IBinaryTreeNode<T> const* right() const = 0;
virtual std::unique_ptr<IBinaryTreeNode<T>>& left() = 0;
virtual std::unique_ptr<IBinaryTreeNode<T>>& right() = 0;
virtual void replace(T const &) = 0;
virtual void replace(T &&) = 0;
};
template<class T>
struct BinaryTreeNode : IBinaryTreeNode<T> {
// can be replaced to mock child creation:
std::function<std::unique_ptr<IBinaryTreeNode<T>>()> factory
= {[]{return std::make_unique<BinaryTreeNode<T>>();} };
// left and right kids:
std::unique_ptr<IBinaryTreeNode<T>> pleft;
std::unique_ptr<IBinaryTreeNode<T>> pright;
// data. I'm allowing it to be empty:
std::unique_ptr<T> data;
template<class U>
void insert_helper( U&& t ) {
if (!get()) {
replace(std::forward<U>(t));
} else if (t < *get()) {
if (!left()) left() = factory();
assert(left());
left()->insert(std::forward<U>(t));
} else {
if (!right()) right() = factory();
assert(right());
right()->insert(std::forward<U>(t));
}
}
// not final methods, allowing for balancing:
virtual void insert( T const&t ) override { // NOT final
return insert_helper(t);
}
virtual void insert( T &&t ) override { // NOT final
return insert_helper(std::move(t));
}
// can be empty, so returns pointers not references:
T const* get() const override final {
return data.get();
}
T * get() override final {
return data.get();
}
// short, could probably skip:
template<class U>
void replace_helper( U&& t ) {
data = std::make_unique<T>(std::forward<U>(t));
}
// only left as customization points if you want.
// could do this directly:
virtual void replace(T const & t) override final {
replace_helper(t);
}
virtual void replace(T && t) override final {
replace_helper(std::move(t));
}
// Returns pointers, because no business how we store it in a const
// object:
virtual IBinaryTreeNode<T> const* left() const final override {
return pleft.get();
}
virtual IBinaryTreeNode<T> const* right() const final override {
return pright.get();
}
// returns references to storage, because can be replaced:
// (could implement as getter/setter, but IBinaryTreeNode<T> is
// "almost" an implementation class, some leaking is ok)
virtual std::unique_ptr<IBinaryTreeNode<T>>& left() final override {
return pleft;
}
virtual std::unique_ptr<IBinaryTreeNode<T>>& right() final override {
return pright;
}
};

Forbid copy-construction of base class

I'm wanting to quickly implement what some call an "owner pointer", that is, a smart pointer ensuring unique ownership semantics, while providing "observer" pointers that don't keep the object alive, but can test whether it is.
The most straightforward way I'm trying to do it is to subclass std::shared_ptr, and disable its copy-construction so that no other pointer can actually share the object.
This is what I have for now :
#include <memory>
#include <iostream>
template <class T>
struct owner_ptr : public std::shared_ptr<T> {
// Import constructors
using std::shared_ptr<T>::shared_ptr;
// Disable copy-construction
owner_ptr(owner_ptr<T> const&) = delete;
// Failed attempt at forbidding what comes next
operator std::shared_ptr<T> const&() = delete;
};
struct Foo {
Foo() {
std::cout << "Hello Foo\n";
}
~Foo() {
std::cout << "G'bye Foo\n";
}
void talk() {
std::cout << "I'm talkin'\n";
}
};
owner_ptr<Foo> fooPtr(new Foo);
int main(int, char**) {
// This should not compile, but it does.
std::shared_ptr<Foo> sptr = fooPtr;
// Simple tests
fooPtr->talk();
(*fooPtr).talk();
// Confirmation that two pointers are sharing the object (it prints "2").
std::cout << sptr.use_count() << '\n';
}
I've been pulling my hair on this one. How do I forbid the copy-construction of a std::shared_ptr from my owner_ptr ? I'm not fond of inheriting privately and then importing everything from std::shared_ptr...
I don't think subclassing std::shared_ptr is the way to go. If you really wanted to do it properly I think you should implement it yourself including all the reference counting. Implementing a smart pointer is not actually that hard.
However, in most cases, if you just want something that meets your needs use composition.
I was curious about what you were trying to do, I'm not convinced it is a good idea but I had a go at implementing a OwnerPointer and ObserverPointer pair using composition:
#include <memory>
#include <iostream>
struct Foo {
Foo() {std::cout << "Hello Foo\n"; }
~Foo() { std::cout << "G'bye Foo\n"; }
void talk() { std::cout << "I'm talkin'\n"; }
};
template <class T>
class ObserverPointer; // Forward declaration.
template<class T>
class OwnerPointer; // Forward declaration.
// RAII object that can be obtained from ObserverPointer
// that ensures the ObserverPointer does not expire.
// Only operation is to test validity.
template <class T>
class ObserverLock {
friend ObserverPointer<T>;
private:
std::shared_ptr<T> impl_;
ObserverLock(const std::weak_ptr<T>& in) : impl_(in.lock()) {}
public:
// Movable.
ObserverLock(ObserverLock&&) = default;
ObserverLock& operator=(ObserverLock&&) = default;
// Not copyable.
ObserverLock& operator=(const ObserverLock&) = delete;
ObserverLock(const ObserverLock&) = delete;
// Test validity.
explicit operator bool() const noexcept { return impl_ != nullptr;}
};
template <class T>
class ObserverPointer {
private:
std::weak_ptr<T> impl_;
T* raw_;
public:
ObserverPointer(const OwnerPointer<T>& own) noexcept : impl_(own.impl_), raw_(own.get()) {}
T* get() const { return raw_; }
T* operator->() const { return raw_; }
T& operator*() const { return *raw_; }
ObserverPointer() : impl_(), raw_(nullptr) { }
ObserverPointer(const ObserverPointer& in) = default;
ObserverPointer(ObserverPointer&& in) = default;
ObserverPointer& operator=(const ObserverPointer& in) = default;
ObserverPointer& operator=(ObserverPointer&& in) = default;
bool expired() { return impl_.expired(); }
ObserverLock<T> lock() { return ObserverLock<T>(impl_); }
};
template <class T>
struct OwnerPointer {
friend ObserverPointer<T>;
private:
std::shared_ptr<T> impl_;
public:
// Constructors
explicit OwnerPointer(T* in) : impl_(in) {}
template<class Deleter>
OwnerPointer(std::unique_ptr<T, Deleter>&& in) : impl_(std::move(in)) { }
OwnerPointer(std::shared_ptr<T>&& in) noexcept : impl_(std::move(in)) { }
OwnerPointer(OwnerPointer<T>&&) noexcept = default;
OwnerPointer(OwnerPointer<T> const&) = delete;
// Assignment operators
OwnerPointer& operator=(OwnerPointer<T> const&) = delete;
OwnerPointer& operator=(OwnerPointer<T>&&) = default;
T* get() const { return impl_.get(); }
T* operator->() const { return impl_.get(); }
T& operator*() const { return *impl_; }
explicit operator ObserverPointer<T>() const noexcept { return ObserverPointer<T>(impl_);}
explicit operator bool() const noexcept { return impl_;}
};
// Convenience function equivalent to make_shared
template <class T, class... Args>
OwnerPointer<T> make_owner(Args && ...args) {
return OwnerPointer<T>(new T(std::forward<Args>(args)...));
}
int main() {
auto owner = make_owner<Foo>();
ObserverPointer<Foo> observer = owner;
auto lock = observer.lock();
if (lock)
observer->talk();
}
Live demo.
It probably needs some work and it doesn't offer the full feature set of std::shared_ptr & std::weak_ptr but then in most cases it won't need to, just create what you need.
I've stretched the definition of "unique ownership" by offering an RAII ObserverLock object that can only be used to keep the ObserverPointer alive. Technically it "owns" the pointer but it is very restricted in what it can do and you can't create more than one "OwnerPointer".

Resources