简体   繁体   中英

How to use transform_iterator of unique_ptr in std algorithm

I am trying to call lower_bound on transformed iterators of a vector<unique_ptr>>. Similar questions had been asked previously on SO. This one is slightly more complex that solutions to other problems are not readily applicable.

The problem is the same. The std implementation calls unique_ptr operator= when it assigns __first to __middle during the search. In this example, a list of transformed objects (int->double) are searched to locate the element equal to or greater than the input (double).

int main ()
{
  vector<unique_ptr<int>>v {
    std::make_unique<int>(0),
    std::make_unique<int>(1),
    std::make_unique<int>(2),
    std::make_unique<int>(3),
    std::make_unique<int>(4),
  };

  auto transFunc = [](const unique_ptr<int>& m) -> double {
    return (*m) * 2.;
  }; 
  auto first = boost::make_transform_iterator(begin(v), transFunc);
  auto last = boost::make_transform_iterator(end(v), transFunc);  

  auto i = lower_bound(first, last, 5.);

  return 0;
}

I also tried using move_iterator's.

  auto transFunc = [](unique_ptr<int>&& m) -> double {
    return (*m) * 2.;
  }; 
  auto first = boost::make_transform_iterator(
      make_move_iterator(begin(v)), transFunc);
  auto last = boost::make_transform_iterator(
      make_move_iterator(end(v)), transFunc);  

It seems like boost didn't carry the right-valueness forward in the transformed iterators.

The code used to work in VS2013 but doesn't work in VS2015 or GNU.

The lambda isn't copyable, and by default transform_iterator does retain a copy of the callable.

Simple solution: std::ref or std::cref :

Live On Coliru

#include <memory>
#include <boost/iterator/transform_iterator.hpp>
#include <vector>

int main ()
{
    auto transFunc = [](const std::unique_ptr<int>& m) -> double { return (*m) * 2; }; 

    std::vector<std::unique_ptr<int>> v;
    v.push_back(std::make_unique<int>(0));
    v.push_back(std::make_unique<int>(1));
    v.push_back(std::make_unique<int>(2));
    v.push_back(std::make_unique<int>(3));
    v.push_back(std::make_unique<int>(4));

    auto first = boost::make_transform_iterator(begin(v), std::cref(transFunc));
    auto last  = boost::make_transform_iterator(end(v), std::cref(transFunc));  
    auto i = lower_bound(first, last, 5.);
}

Alternatively:

Create a copyable calleable instead:

struct { double operator()(const std::unique_ptr<int>& m) const { return (*m) * 2; }; } transFunc;

Live On Coliru

Bonus

Live On Coliru

#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/phoenix.hpp>
#include <vector>
#include <memory>

using namespace boost::adaptors;
using namespace boost::phoenix::arg_names;

int main () {
    std::vector<std::unique_ptr<int>> v(5);
    boost::generate(v, [n=0]() mutable { return std::make_unique<int>(n++); });

    auto i = boost::lower_bound(
            v |
            indirected |
            transformed(2. * arg1), 5.);
}

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