如何在 <T> 的容器上使用 std::sample 返回指针容器 <T*>?

Posted

技术标签:

【中文标题】如何在 <T> 的容器上使用 std::sample 返回指针容器 <T*>?【英文标题】:How can I return a container of pointers <T*> using std::sample on a container of <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 【参考方案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 合法

以上是关于如何在 <T> 的容器上使用 std::sample 返回指针容器 <T*>?的主要内容,如果未能解决你的问题,请参考以下文章

如何在容器中指定模板化别名'泛型类型

c++11 用 std::swap vs operator=(T&&) 清除容器

如何使用 std::optional<T>::emplace 的第二个重载

std::vector<std::array<T, N>> 或 std::array<std::vector<T>,N> 类型的数组如何存储在内存中?

如何创建一个可以将 std::unique_ptr 作为模板参数正常工作的容器?

setpriority_queue等容器如何加入自定义类