Reputation: 6679
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.
Upvotes: 4
Views: 591
Reputation: 392931
The lambda isn't copyable, and by default transform_iterator
does retain a copy of the callable.
Simple solution: std::ref
or std::cref
:
#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.);
}
Create a copyable calleable instead:
struct { double operator()(const std::unique_ptr<int>& m) const { return (*m) * 2; }; } transFunc;
#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.);
}
Upvotes: 2