简体   繁体   English

保留std :: set或std :: unordered_set上的插入顺序

[英]Preserving insertion order on a std::set or std::unordered_set

Before marking this as duplicate, I have been here , here , and here, a duplicate of the first . 在将其标记为重复之前,我来到这里这里第一个副本

I'm aware of boost::multi_index , and use an environment where I lack it, and that a std::unordered_set is not bound to store elements in a deterministic insertion order. 我知道boost::multi_index ,并使用我缺少它的环境,并且std::unordered_set没有绑定以确定的插入顺序存储元素。

I find the concept of using two containers, say an additional std::vector as uncouth. 我发现使用两个容器的概念,比如另一个std::vector就像粗糙。

What I would love is a solution involving a comparator that I can use in a std::set 's template parameters (clarification, this could be a trivial functor struct, containing a bool operator()() overload, a regular function, or a lambda). 想要的是一个涉及比较器的解决方案,我可以在std::set的模板参数中使用(澄清,这可能是一个简单的函子结构,包含bool operator()()重载,常规函数,或者一个lambda)。 Is it possible? 可能吗?

Addenda 附加物

  1. Initialization must occur through a std:: container's begin iterator/end iterator constructor, such as in this snippet. 初始化必须通过std :: container的begin iterator / end iterator构造函数进行,例如在此代码段中。

     std::string str; cin >> str; std::set<char>(str.begin(), str.end()); 
  2. Also, another interesting use-case would be to create a dumb hash wrapping functor that allows insertion order to be pushed in to a std::unordered_set 's template parameter. 另外,另一个有趣的用例是创建一个哑哈希包装函子,允许将插入顺序推送到std::unordered_set的模板参数。

You cannot directly have a lambda expression as the set's template parameter, because a lambda expression is a value, and the set's template parameter is a type. 您不能直接将lambda表达式作为set的模板参数,因为lambda表达式是一个值,而set的模板参数是一个类型。 The obvious correction of the question, whether a construction using a lambda and decltype can work, leads to the interesting problem that a lambda expression denotes a unique type (a "closure type"), so you can never make two separate lambda expressions of the same closure type.* 问题的明显更正,无论使用lambda和decltype的构造是否可行,都会导致一个有趣的问题,即lambda表达式表示一个唯一类型(“闭包类型”),因此你永远不能创建两个单独的lambda表达式。相同的封闭类型。*

However, in a more abstract sense what you want can be achieved in a local context using template argument deduction, for example: 但是,在更抽象的意义上,您可以使用模板参数推导在本地上下文中实现您想要的内容,例如:

template <typename F>
int f(int* first, int* last, F comp)
{
    std::set<int, F> s(comp);
    while (first != last) s.insert(*first++);
    ...
}

Now you can call f with a lambda expression as the argument, thus effectively "using a lambda as the set's comparator". 现在,您可以使用lambda表达式作为参数调用f ,从而有效地“使用lambda作为集合的比较器”。 Or, for a simpler example, you could just have a named variable for the lambda (putting all the template deduction into a single auto : 或者,对于一个更简单的示例,您可以为lambda创建一个命名变量(将所有模板推导放入单个auto

auto comp = [](...) { ... };
std::set<int, decltype(comp)> s(comp);

*) There is a proposal to allow lambdas in unevaluated contexts to address this point, but its outlook is uncertain. *)有一项建议允许在未评估的情境中使用lambdas来解决这一问题,但其前景尚不确定。 It has interesting side effects like making closure types affect name mangling. 它有一些有趣的副作用,比如使闭包类型影响名称修改。

An adt that preserves the order of insertion is an std::vector. 保留插入顺序的adt是std :: vector。 You can just as easily wrap it like this to get an std::set-like behavior: 您可以像这样轻松地包装它以获得类似std :: set的行为:

#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>

using namespace std;

template < typename T >
class VectorSet : public vector<T> {
public:
    using iterator = typename vector<T>::iterator;
    using value_type = typename vector<T>::value_type;

    pair<iterator, bool> insert (const value_type& val) {
        auto it = ::find(this->begin(), this->end(), val);
        if (it == this->end())
            it = ::vector<T>::insert(this->end(), val);

        return pair<iterator, bool>(it, true);
    }
};

int main()
{
    VectorSet<int> my;
    my.insert(1);
    my.insert(4);
    my.insert(3);
    my.insert(4);

    for (auto & v : my) {
        cout << v << endl;
    }

    return 0;
}

You cannot, unless you use additional indexes. 除非您使用其他索引,否则不能。 Two approaches: 两种方法:

1. using an explicit index 1.使用显式索引

Live On Coliru 住在Coliru

#include <set>
#include <vector>
#include <functional>
#include <algorithm>

using namespace std;

#include <iostream>

string read_word() {
    string str;
    cin >> str;
    return str;
}

int main() {
    using Ref = std::reference_wrapper<char const>;

    auto const str = read_word();
    std::cout << "Word: "   << str << "\n";

    auto v = [&]() -> vector<Ref> {
        set<Ref> u(str.begin(), str.end());
        return {u.begin(), u.end()};
    }();

    std::cout << "Unique: " << string(v.begin(), v.end()) << "\n";

    auto pos = [str](char ch) { return str.find(ch); };
    std::sort(v.begin(), v.end(), [pos](auto& a, auto& b) { return pos(a) < pos(b); });

    std::cout << "Insertion: " << string(v.begin(), v.end()) << "\n";
}

Prints eg 打印例如

Word: pineapple
Unique: aeilnp
Insertion: pineal

2. using Boost Multi-Index 2.使用Boost Multi-Index

Same deal 同样的交易

Live On Coliru 住在Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>

namespace bmi = boost::multi_index; 

using Index = bmi::multi_index_container<char, 
      bmi::indexed_by<
          bmi::sequenced<>,
          bmi::ordered_unique<bmi::tag<struct unique>, bmi::identity<char> >
      > > ;

#include <iostream>

std::string read_word() {
    std::string str;
    std::cin >> str;
    return str;
}

int main() {

    auto const str = read_word();
    std::cout << "Word: "   << str << "\n";

    Index idx(str.begin(), str.end());

    std::cout << "Insertion: " << std::string(idx.begin(), idx.end()) << "\n";

    auto& u = idx.get<unique>();
    std::cout << "Unique: " << std::string(u.begin(), u.end()) << "\n";
}

Prints 打印

Word: pineapple
Insertion: pineal
Unique: aeilnp

I thought a weird solution (though not one involving any sets) could be to use a std::map of the element type and std::time_point as the key type. 我认为一个奇怪的解决方案(虽然不涉及任何集合)可能是使用元素类型的std::mapstd::time_point作为键类型。 That will ensure insertion order if not anything at all. 这将确保插入顺序,如果没有任何东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM