3

I have a class Container, having a data member.

std::vector< std::unique_ptr<Sum_Function> > Functions;

This is how i add value to the vector.

MaxSize is the child of Sum_Function.

void WavefrontRenderer::AddMaxSize()
 {
     Container cont;
     std::unique_ptr<Sum_Function> ptrMaxSize = std::make_unique<SumMaxSize>();
     cont.AddFunction(ptrMaxSize);
}

this is the defination for Function in the Container class.

void Container::AddFunction(std::unique_ptr<Sum_Function> &func)
 {
   std::unique_ptr< Sum_Function > function(std::move(func));
   this->Functions.push_back(function);
 }

Is this the correct way to add Unique pointer to a vector.

2 Answers2

5

To move an existing Sum_Function subclass object pointer into Functions you can do like this:

void AddFunction(std::unique_ptr<Sum_Function>& func) {
    Functions.emplace_back(std::move(func));
}
...
Container c;
auto f = std::make_unique<SumMaxSize>();
c.AddFunction(f);

... or like this, which requires the argument to be an rvalue reference, which in turn makes the pointer-stealing more obvious:

void AddFunction(std::unique_ptr<Sum_Function>&& func) {
    Functions.emplace_back(std::move(func));
}
...
Container c;
auto f = std::make_unique<SumMaxSize>();
// c.AddFunction(f);                           // Error, f is an lvalue
c.AddFunction(std::move(f));                   // OK, xvalue
c.AddFunction(std::make_unique<SumMaxSize>()); // OK, prvalue

You could also provide a convenience member function in Container for creating it directly in the vector without the middle step:

template<typename T, class... Args>
void emplace_back(Args&&... args) {
    Functions.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
}

And instead of doing make_unique first and calling AddFunction, just:

Container c;
c.emplace_back<SumMaxSize>();

The perfect forwarding in the emplace_back function template above will also make the in-place construction to work for subclasses with constructors that takes arguments:

struct BiasedSumFunc : Sum_Function {
    BiasedSumFunc(int bias) : bias_(bias) {}
private:
    int bias_;
};
... 
Container c;
c.emplace_back<BiasedSumFunc>( -5 );
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • i have no arguments to pass can i i do this. –  Aug 29 '19 at 06:08
  • template void Container::AddFunction() { this->Functions.emplace_back(std::make_unique()); } –  Aug 29 '19 at 06:08
  • 1
    @sam Sure, if you'd like to make sure that only default constructed `Sum_Function` sub-classes are placed in the vector via `AddFunction`, but that puts restrictions on the users. If someone inherits from `Sum_Function` and adds a constructor that takes arguments, the user would not be able to use that `AddFunction` to construct his/her sub-class object in-place. Instead, the user would have to first `make_unique(argument)` and then call an overload of `AddFunction` that steals the pointer (like my second alternative above). – Ted Lyngmo Aug 29 '19 at 06:28
0

A unique_ptr can't be copied, only moved. So when calling push_back(), you need to use the overload that moves from an rvalue, not the one that copies from an lvalue:

void Container::AddFunction(std::unique_ptr<Sum_Function> &func)
{
    std::unique_ptr< Sum_Function > function(std::move(func));
    this->Functions.push_back(std::move(function));
}

Which makes the local function variable redundant, so you can remove it:

void Container::AddFunction(std::unique_ptr<Sum_Function> &func)
{
    this->Functions.push_back(std::move(func));
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770