2

from what I've discerned by reading through most of the questions on here that pop up when looking up unique pointers, this seems to me like it should provide the behavior described by the Builder pattern.

I want any instance of Builder (or its sub-classes, since it doesn't implement any procedures for modifying the object under construction) to own the object under construction until Close returns the unique pointer to the caller, at which point the caller takes ownership.

Builder.h

template <class type> class Builder
{
public:
    ~Builder();
    unique_ptr<type> Close();
protected:
    Builder();
    unique_ptr<type> _uptr;
};

Builder.cpp

template<class type> Builder<type>::Builder()
{
    uptr = make_unique<type>();
}

template<class type> Builder<type>::~Builder()
{}

template<class type> unique_ptr<type> Builder<type>::Close()
{
    return uptr;
}

Do I understand the semantics of passing a unique pointer by value?

(includes and namespaces omitted for brevity / legibility)

Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
ophilbinbriscoe
  • 137
  • 1
  • 8

2 Answers2

3

std::unique_ptr cannot be copied. Instead, you will have to move uptr in order to properly transfer ownership of the underlying pointer.

template<class type> unique_ptr<type> Builder<type>::Close()
{
    return std::move(uptr);
}
Robert Prévost
  • 1,667
  • 16
  • 22
1

Do I understand the semantics of passing a unique pointer by value?

You can move from a unique_ptr: std::move(this->_uptr)

Be cautious with moves as they invalidate the contents of the original object.

I've completed your example to exemplify the threat:

#include <iostream>
#include <memory>

template<typename T> class Builder
{
public:
    virtual ~Builder() {}
    std::unique_ptr<T> Close();
protected:
    Builder() {}
    std::unique_ptr<T> _uptr;
};

class IntBuilder: public Builder<int>
{
public:
    IntBuilder() : Builder<int>() {
        this->_uptr = std::unique_ptr<int>(new int);
    }
    void setValue(int x) {
        *(this->_uptr) = x;
    }
    std::unique_ptr<int> Close() {
        return std::move(this->_uptr);
    }
};

int main() {
    IntBuilder b;
    b.setValue(3);
    auto i = b.Close();
    std::cout << *i << std::endl; // OK
    auto i2 = b.Close();
    std::cout << *i2 << std::endl; // Segmentation fault
}

Although this->_uptr was moved within IntBuilder::Close(), the compiler will not warn you about the Segfault potential.


Furthermore I'd recommend just using T Builder<T>::Close() instead of unique_ptr<T> Builder<T>::Close() as the latter just limits flexibility of the class.

Also why not have sub-classes manage the instance they're creating. If the instance they're creating cannot be mutated the sub-class needs to store information about the instance until its creation (at Close()) and will unnecessarily carry along the unique_ptr<T>.

Here's how I'd alter the Builder class:

template<typename T> class Builder
{
public:
    virtual ~Builder() {}
    T&& Close();
protected:
    Builder() {}
};
ambiso
  • 559
  • 3
  • 10
  • I was definitely aware of the rigid nature of my original definition. Part of it is because this will ultimately be part of a larger deliverable for a course, the grading criteria for which are entirely focused on our understanding and use of design patterns through C++. – ophilbinbriscoe Sep 15 '16 at 13:23
  • I am also working in a team, and all of us are new to the language, so having strict usage patterns for our more primitive user defined types seemed like something that might eliminate ambiguity for my peers. I see the motivation behind doing it this way, though. Reading this: http://stackoverflow.com/questions/5481539/what-does-t-double-ampersand-mean-in-c11 – ophilbinbriscoe Sep 15 '16 at 13:23
  • I am still a bit fuzzy on what exactly is happening when Close returns. If the builder instance was a MyClassBuilder, returning an object instance with lots of members, some of the values, some of them pointers, what happens to the ownership? Doesn't T&& avoid copying by moving the internals? Or does it copy, since the instance of T is not a tempory? – ophilbinbriscoe Sep 15 '16 at 13:23
  • I am hesitant to modify my implementation to one that I don't fully understand. – ophilbinbriscoe Sep 15 '16 at 13:23
  • To anyone reading, the following article clarified the semantics of T&& for me. https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers – ophilbinbriscoe Sep 15 '16 at 14:27