7

When I compile the below code, I get a compilation error:

std::vector<std::unique_ptr<boxIndex>> tmpVec;
for(const auto& it: hrzBoxTmpMap){
    for(const auto& it2: hrzBoxVec){
        std::copy_if(hrzBoxVec.begin(), hrzBoxVec.end(), tmpVec.begin(), [&](std::unique_ptr<boxIndex>& p)
        {
            return !(it.second == p->getTop() &&
                     it.first != p->getLeft() );
        });
    }
}

The compilation error is:

/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_algo.h: 

> In instantiation of ‘_OIter std::copy_if(_IIter, _IIter, _OIter,
> _Predicate) [with _IIter = __gnu_cxx::__normal_iterator<std::unique_ptr<boxIndex>*, std::vector<std::unique_ptr<boxIndex> > >; _OIter =
> __gnu_cxx::__normal_iterator<std::unique_ptr<boxIndex>*, std::vector<std::unique_ptr<boxIndex> > >; _Predicate =
> smoothHrzIndexing(std::vector<std::unique_ptr<boxIndex>
> >&)::<lambda(std::unique_ptr<boxIndex>&)>]’: test_word_2.cpp:282:5:   required from here
> /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_algo.h:990:6:
> error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>&
> std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&)
> [with _Tp = boxIndex; _Dp = std::default_delete<boxIndex>]’ In file
> included from
> /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/memory:86:0,
>                  from test_word_2.cpp:8: /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/unique_ptr.h:263:19:
> error: declared here

Can someone help me on this?

localhost
  • 375
  • 1
  • 3
  • 15
user3665224
  • 1,349
  • 3
  • 16
  • 34
  • 7
    You cant copy a unique_ptr you need to move them. – quantdev Jul 21 '14 at 14:33
  • Do you want the new vector to have unique_ptrs to copies of the original objects? – Neil Kirk Jul 21 '14 at 14:34
  • I am aware of Moving unique pointers. But not sure where to place std::move in above statement. Pls confirm. – user3665224 Jul 21 '14 at 14:34
  • Neil, I want just new vector to hold the objects instead of old vector. That is, i would not need old vector anymore – user3665224 Jul 21 '14 at 14:36
  • 2
    Interesting problem. I don't know how to do this with std algorithm. There is copy_if but not move_if. You could swap the vectors and use remove_if on the new vector. – Neil Kirk Jul 21 '14 at 14:38
  • But, i am afraid copying vector is costly and I would not want it. I would prefer to iterate through my present vector(of unique pointers) and remove certain elements. I even tried below statement. But in vain. hrzBoxVec.erase(std::remove_if(hrzBoxVec.begin(), hrzBoxVec.end(), [&](std::unique_ptr & p) { return std::find(hrzBoxTmpMap.begin(), hrzBoxTmpMap.end(), p.get())!= hrzBoxTmpMap.end(); } ), hrzBoxVec.end()); – user3665224 Jul 21 '14 at 14:45
  • 2
    Just swap and remove_if, as suggested by @NeilKirk. – juanchopanza Jul 21 '14 at 14:48
  • 2
    You don't need to copy the vector, swap is fast. – Neil Kirk Jul 21 '14 at 14:50
  • Although this issue is solved by properly moving the pointers, sometimes a copy is really wanted. Few hints here: http://stackoverflow.com/questions/23535719/add-a-deep-copy-ctor-to-stdunique-ptrmy-type – DarioP Jul 21 '14 at 15:09
  • `v2.swap(v1);` or you'll have to clone the objects pointed to. – user1095108 Jul 21 '14 at 15:14

3 Answers3

7

What you could do is to use std::move_iterator<...> using, e.g., something like below (this is a SSCCE demonstrating the crucial point:

#include <iostream>
#include <iterator>
#include <vector>
#include <memory>

int main()
{
    std::vector<std::unique_ptr<int>> boxVec;
    boxVec.emplace_back(new int(1));
    boxVec.emplace_back(new int(17));
    boxVec.emplace_back(new int(3));
    std::vector<std::unique_ptr<int>> tmpVec;
    std::copy_if(std::make_move_iterator(boxVec.begin()),
                 std::make_move_iterator(boxVec.end()),
                 std::back_inserter(tmpVec),
                 [&](std::unique_ptr<int> const& p){
                     return *p == 17; });
    for (auto const& x: boxVec) {
        (x? std::cout << *x: std::cout << "<null>") << " ";
    }
    std::cout << "\n";
}

Dereferencing a std::move_iterator<It> will return a suitable rvalue of the iterator's value type. Since the rvalue is obtained using std::move(*it), it is a reference. That is, the value isn't stolen until the value is actually moved. The comparison uses a const&, i.e., it won't steal the value. The assignment will become an rvalue assignment. The code also uses std::back_inserter() to arrange for enough elements to be in the destination.

I don't think this is really a solid solution but I also don't think there is an algorithm like std::move_if() (or a customization of any algorithm resulting in the same behavior). To really deal with conditionally moving objects I think you'd different access mechanism for the values passed into the predicate and the the way objects are assigned (property maps would address these issues but there is no proposal, yet, to add them to the C++ standard).

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 1
    you could use `remove_copy_if` instead of `copy_if`, so the moved elements can be deleted from the old vector. – MatthiasB Jul 21 '14 at 15:13
3

There is no direct way of doing this, but you can chain some STL commands to achieve what you want:

  • std::stable_partition or std::partition to split your container in half.
  • std::move to move the values you want to move to your new vector
  • vector.erase to delete the old, invalid unique_ptr

In the end you have a clean source vector (all moved, invalid entries are removed) and a clean target vector.

this can look something like that:

std::vector<std::unique_ptr<int>> source, target;
// ... fill data
auto it = std::stable_partition(begin(source),end(source),[](std::unique_ptr<int> const& val) {
    return *val < 3; // your negated condition
});
std::move(it,end(source),std::back_inserter(target));
source.erase(it,end(source));

Here is a live example

MatthiasB
  • 1,759
  • 8
  • 18
0

std::unique_ptr is movable, not copyable.
So, you can't use std::copy_if() with unique_ptr instances.

If you really want to use std::copy_if(), then you may use std::shared_ptr instead of std::unique_ptr, and then if you want to get rid of the old vector content, just destroy it using e.g. vector::clear().

Or, if shared_ptr is too much overhead for you, or in any case you just want a vector of unique_ptrs, then you may want to consider doing an in-place removing of unwanted vector elements using std::remove_if() and the erase-remove idiom.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162