如何在 std 算法中使用 unique_ptr 的 transform_iterator

Posted

技术标签:

【中文标题】如何在 std 算法中使用 unique_ptr 的 transform_iterator【英文标题】:How to use transform_iterator of unique_ptr in std algorithm 【发布时间】:2018-04-02 20:31:24 【问题描述】:

我正在尝试在 vector<unique_ptr>>. 的转换迭代器上调用 lower_bound 之前在 SO 上已提出类似问题。这个稍微复杂一点,其他问题的解决方案不容易适用。

问题是一样的。 std 实现在搜索期间将 __first 分配给 __middle 时调用 unique_ptr operator=。在此示例中,搜索转换对象列表 (int->double) 以定位等于或大于输入 (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;

我也尝试过使用 move_iterator 的。

  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);  

似乎 boost 没有在转换后的迭代器中实现正确的价值。

该代码在 VS2013 中可以运行,但在 VS2015 或 GNU 中无法运行。

【问题讨论】:

【参考方案1】:

lambda 不可复制,默认情况下transform_iterator 确实保留可调用的副本。

简单解决方案:std::refstd::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.);

或者:

改为创建一个可复制的可调用对象:

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

Live On Coliru

奖金

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.);

【讨论】:

刚刚意识到,如果我们使用 phoenix,我们不需要单独进行间接寻址:lower_bound(v | transformed((*arg1) * 2.0), 5.) @CandyChiu 当然,虽然它也可能有助于扼杀性能

以上是关于如何在 std 算法中使用 unique_ptr 的 transform_iterator的主要内容,如果未能解决你的问题,请参考以下文章

如何在构造函数中使用删除器初始化 std::unique_ptr?

如何将 std::sort() 与 std::unique_ptr<[]> 一起使用?

如何在结构上使用 std::unique_ptr?

如何将 std::unique_ptr 初始化为引用

如何将 unique_ptr 与 std::copy 一起使用?

如何从 C++14 中的广义 lambda 捕获返回包含 std::unique_ptr 的 std::function?