Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++11

The Cow Class: A Copy-On-Write C++ Template

4.74/5 (9 votes)
9 Feb 2020CPOL 15.1K  
Copy your objects only when needed
Often you have an object that would like to be shared in read-only form across the callers, but you would like someone to create copies when it's modified. Here is a small template that will do that for you automatically.

This is part of my multithreading library at github, here.

Code

template <typename T> class cow
{
private:
    mutable std::shared_ptr<T> t;

public:

    template< typename ...Args>
    cow(Args ... args)  { t = std::make_shared<T>(args...); }

    cow(std::shared_ptr<T> t2)
    {
        t = t2;
    }

    operator const std::shared_ptr<T>() const { return t; }
    const T operator *()
    {
        return *t;
    }

    // Write
    shared_ptr<T> operator ->()
    {
        if (t.use_count() == 1)
            return t;

        std::shared_ptr<T> t2 = std::make_shared<T>(*t);
        t = t2;
        return t;
    }

    void write(std::function<void(std::shared_ptr<T> )> f)
    {
        f(operator ->());
    }

};

You have the object to be manipulated in a shared_ptr<> form. The class constructor's take either an existing pointer or they can construct a new object with the aid of variadic template functions. 

When using this class to read data, nothing happens:

 struct FOO
    {
	    int a1 = 0;
	    int a2 = 0;
    };

std::shared_ptr<FOO> x = std::make_shared<FOO>();
cow<FOO> c1(x); // gets the pointer
const FOO& j1 = *c1;
cow<FOO> c2 = c1;

All the above is serviced through  the const operator/const T* cast functions. 

When writing the data, the class duplicates the pointer before writing to it:

c1->a2 = 2; // 1 copy 

Now c1 does not anymore contain the same as the original x, which remains untouched.

c1->a4 = 2; // No copy

There should be another copy but the previous one dissappears because it had a ref of 1, so it's all optimized out.

c2.write([](std::shared_ptr<FOO> t)
 { 
    t->a1 = 10; 
    t->a2 = 20; 
 });

This would allow you to write once without creating uneccesary copies.

Have fun using it!

History

9 - 2 - 2020: First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)