【问题标题】:How can I return a container of pointers <T*> using std::sample on a container of <T>?如何在 <T> 的容器上使用 std::sample 返回指针容器 <T*>?
【发布时间】:2017-10-16 18:29:18
【问题描述】:

我有一个 vector&lt;T&gt; input,我想通过 STL C++17 (http://en.cppreference.com/w/cpp/algorithm/sample) 中的 std::sample 算法从中获取 n 个随机选择的元素。如果resultsvector&lt;T&gt; 类型,则代码可以正常工作。

代码示例 1(未返回指针)

auto getSamples(unsigned int noSamples, const vector<T> &input)
{
    vector<T> results;
    std::mt19937 twisterEngine;
    std::sample(input.begin(), input.end(), std::back_inserter(results),
        noSamples, twisterEngine);
    return results;
}

但是,我不是在寻找存储在 input 中的元素的值/副本,而是希望获得指向 n 采样元素的指针。有什么技巧可以让我仅使用标准 c++ 代码(例如,不使用 boost 库等)获取 vector&lt;T*&gt; results 返回的指针吗?我需要如何调整以下代码才能完成?

代码示例 2(打算返回指针)

auto getSamples(unsigned int noSamples, const vector<T> &input)
{
    vector<T*> results;
    std::mt19937 twisterEngine;
    std::sample(input.begin(), input.end(), std::back_inserter(results),
        noSamples, twisterEngine);
    return results;
}

【问题讨论】:

  • 我会返回指向样本的指针,因为input 是一个函数局部向量,所以你会传回一个悬空指针向量。唯一安全的情况是inputconst&amp; 传递,并且您可以保证input 将在results 的生命周期内存在
  • 调整了代码,使输入由 const& 传递。但是,我想在结果中返回指针。
  • 我不认为sample 是正确的算法。我会用所有可用的指针填充一个向量,然后选择该向量随机排列的第一个 n 元素
  • ...erm 或在包含所有指针的向量上使用sample(您需要先填充它)。有点困惑,因为我第一次看到sample

标签: c++ algorithm pointers iterator c++17


【解决方案1】:

您只需要一个OutputIterator。它实际上并没有必须 像迭代器一样发出一些东西。它可以只是……完全做其他事情。比如,调用一个函数。

#include <iterator>

template <class F>
struct function_output_iterator {
    F f;

    using iterator_category = std::output_iterator_tag;
    using value_type = void;
    using difference_type = void;
    using pointer = void;
    using reference = void;

    function_output_iterator& operator++() { return *this; }
    function_output_iterator& operator*() { return *this; }
    function_output_iterator& operator++(int) { return *this; }

    template <class U,
        std::enable_if_t<!std::is_base_of<
            function_output_iterator, std::decay_t<U>>{}, int> = 0>
    void operator=(U&& u) {
        f(std::forward<U>(u));
    }
};

template <class F>
function_output_iterator(F ) -> function_output_iterator<F>;

然后,你可以做任何你想做的任意操作:

auto getSamples(unsigned int noSamples, const vector<T> &input)
{
    vector<T*> results;
    results.reserve(noSamples);

    std::mt19937 twisterEngine;
    std::sample(input.begin(), input.end(),
        function_output_iterator{[&](T const& elem){ results.push_back(&elem); }, // <==
        noSamples, twisterEngine);
    return results;
}

【讨论】:

  • 或者 boost::make_function_output_iterator 如果你不想自己写(啊,我现在看到 OP 说没有 Boost,但仍然可能对没有此类限制的其他人有用)跨度>
  • @Praetorian 我注意到 Boost 的 operator* 返回一个已实现 operator= 的代理 - 你知道他们为什么这样做吗? (出于某种原因,我认为它比这更正确)
  • 不知道,Google 搜索出现了 this proposal(来自 Boost 作者),上面写着 这个迭代器的动机是创建一个符合要求的输出迭代器并非易事,尤其是因为正确的实现通常需要一个代理对象。,但我想不出有什么不同的情况。还找到了this comment,所以你这个问题已经有一段时间了:)
  • @Praetorian HA!确实,也许我应该问一个适当的问题或其他什么。
  • @Barry:它更正确,因为它阻止你写 out_it = val****out_it = val 并且让它们意味着同样的事情。你真的应该做 boost 所做的事情,这使得只有 *out_it = val 合法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-25
  • 1970-01-01
  • 1970-01-01
  • 2014-12-04
  • 1970-01-01
  • 2015-04-30
  • 1970-01-01
相关资源
最近更新 更多