简体   繁体   中英

Modern C++ builder pattern without unique_ptr

I'd want to implement a builder pattern in modern C++. Coming from Java background, this is something I'd like to emulate:

// Usage
FooBuilder builder;
builder.setArg1(a);
builder.setArg2(b);
Foo foo = builder.build();

// Implementation
public class FooBuilder {
    // ...
    public Foo build() {
        return new Foo(a, b);
    }
}

Typical older textbooks just advice one to do it like that in C++:

class FooBuilder {
    // ...
    Foo* build() {
        return new Foo(m_a, m_b);
    }
}

which is obviously not a good idea, as dealing with raw pointers might be error-prone. The best I've came up with so far is using std::unique_ptr manually:

class FooBuilder {
    // ...
    std::unique_ptr<Foo> build() {
        return std::make_unique<Foo>(m_a, m_b);
    }
}

// Usage
auto fooPtr = builder.build();
Foo& foo = *fooPtr;
foo.someMethod();

It's better, as it doesn't require manual delete , this two-liner conversion to reference is ugly, and, what's more important, it uses heap allocation, while simple builder-less version would be totally ok with just a simple stack allocation:

Foo foo(..., ...); // <= on stack

Are there any better ways to do that, ie without unique_ptr, or with some sort of on-stack allocation for Foo?

There's no reason why you have to allocate on the heap to use the builder pattern. Just have your build() method return Foo directly:

class FooBuilder {
public:
    Foo build() { // You may consider having a &&-qualified overload
        return Foo{ ..., ... };
    }
};

Generally, if Foo is copy_constructible , then you can just return Foo by value.

#include <type_traits>

class Foo
{
    int i;

public:
    Foo(int i): i(i){}

};

static_assert(std::is_copy_constructible<Foo>::value, "Foo is copy-constructible");

struct FooFactory
{
    //...
    Foo build() {return Foo(1);}
};


int main()
{
    FooFactory factory;
    //...
    Foo foo = factory.build();
}

And new in c++17, is guaranteed copy elision , which means that you can return by value even if the type does not have copy or move constructors:

#include <type_traits>

class Foo
{
    int i;

public:
    Foo(int i): i(i){}
    // regular copy constructors don't exist for whatever reason. 
    Foo() = delete;
    Foo(Foo const& ) =delete;
    Foo(Foo&& ) = delete;
    Foo& operator=(Foo const&) = delete;
    Foo& operator=(Foo&& ) = delete;
};

static_assert(not std::is_copy_constructible<Foo>::value, "Foo is definitely not copy-constructible");


struct FooFactory
{
    //...
    Foo build() {return Foo(1);}
};


int main()
{
    FooFactory factory;
    //...
    Foo foo = factory.build();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM