【问题标题】:Using vector for buckets in boost intrusive在 boost intrusive 中使用存储桶的向量
【发布时间】:2020-11-12 14:12:08
【问题描述】:

如何使用std::vector 管理boost::intrusive::unordered_set 的桶?下面的代码让我做噩梦:

#include <boost/intrusive/unordered_set.hpp>
#include <vector>
#include <cstdint>

namespace BI = boost::intrusive;

struct ValuableData : public BI::unordered_set_base_hook<>
{
    int  x;
    int  y;
    char c;
};

typedef BI::base_hook<BI::unordered_set_base_hook<>> Options;
typedef BI::unordered_bucket<Options>::type     BucketType;
typedef BI::unordered_bucket_ptr<Options>::type BucketPtr;

struct VectorTraits{

    std::vector<BucketType> v;

    BucketPtr bucket_begin(){
        return v.data();
    }

    uint32_t bucket_count() const {
        return v.capacity();
    }
};

struct dumhash{
    uint32_t operator()(const ValuableData &data) const {
        return data.x;
    }
};

struct dumcomp{
    bool operator()(const ValuableData &data, const ValuableData &datb)     const {
    return true;
}
};

typedef BI::unordered_set<ValuableData, 
        BI::hash<dumhash>, 
        BI::equal<dumcomp>,
        BI::bucket_traits<VectorTraits>> MySet;

int main(){

    VectorTraits tr;

    tr.v.reserve(100);
    tr.v.push_back(BucketType()); //.data() may return null is vector is empty.

    MySet set(tr);
    set.rehash(tr);

    return 0;
}

错误消息表明违反了const 限定符。我的boost 版本是 1.73.0,我的编译器 g++ 10.2.0。我必须使用c++17

boost intrusive 手册提到了const_bucket_ptr,但没有说明如何访问该类型。我怀疑这是我的错误的来源,请参阅: https://www.boost.org/doc/libs/1_74_0/doc/html/intrusive/unordered_set_unordered_multiset.html#intrusive.unordered_set_unordered_multiset.custom_bucket_traits

编辑:我用一个更简单的示例替换了之前实际使用的代码,您可以在家里完全编译。这是我的完整错误信息:

In file included from include/boost/intrusive/unordered_set.hpp:18,
                 from src/BoostIntrusiveSet.cpp:9:
include/boost/intrusive/hashtable.hpp: In instantiation of 'void boost::intrusive::hashtable_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual, BucketTraits, SizeType, BoolFlags>::rehash_impl(const bucket_traits&, bool) [with ValueTraits = boost::intrusive::bhtraits<ValuableData, boost::intrusive::slist_node_traits<void*>, boost::intrusive::safe_link, boost::intrusive::dft_tag, 4>; VoidOrKeyOfValue = void; VoidOrKeyHash = dumhash; VoidOrKeyEqual = dumcomp; BucketTraits = VectorTraits; SizeType = long long unsigned int; long long unsigned int BoolFlags = 3; boost::intrusive::hashtable_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual, BucketTraits, SizeType, BoolFlags>::bucket_traits = VectorTraits]':
include/boost/intrusive/hashtable.hpp:2944:13:   required from 'void boost::intrusive::hashtable_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual, BucketTraits, SizeType, BoolFlags>::rehash(const bucket_traits&) [with ValueTraits = boost::intrusive::bhtraits<ValuableData, boost::intrusive::slist_node_traits<void*>, boost::intrusive::safe_link, boost::intrusive::dft_tag, 4>; VoidOrKeyOfValue = void; VoidOrKeyHash = dumhash; VoidOrKeyEqual = dumcomp; BucketTraits = VectorTraits; SizeType = long long unsigned int; long long unsigned int BoolFlags = 3; boost::intrusive::hashtable_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyHash, VoidOrKeyEqual, BucketTraits, SizeType, BoolFlags>::bucket_traits = VectorTraits]'
src/BoostIntrusiveSet.cpp:64:15:   required from here
include/boost/intrusive/hashtable.hpp:3153:73: error: passing 'const bucket_traits' {aka 'const VectorTraits'} as 'this' argument discards qualifiers [-fpermissive]
 3153 |       const bucket_ptr new_buckets      = new_bucket_traits.bucket_begin();
      |                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
compilation terminated due to -Wfatal-errors.

【问题讨论】:

  • 附言。请注意,您的 dumcomp 调用 UB 因为相等的要求是它对应于哈希函数。 (我为我的答案修复了它)
  • 为什么是这个意思? (请记住,当前是一个占位符,用于编译示例)。
  • 如果哈希/相等不匹配,则表示算法有Undefined Behaviour

标签: c++ boost g++ c++17 boost-intrusive


【解决方案1】:

documentation 声明需要 bucket_begin() 的 const 重载:

用户定义的bucket-traits必须满足以下接口:

class my_bucket_traits
{
   bucket_ptr        bucket_begin();
   const_bucket_ptr  bucket_begin() const;
   std::size_t bucket_count() const;
};

我不确定您为什么认为可以安全地使存储桶特征拥有存储桶容器。该示例所做的是将 pointer 存储到存储桶。这解决了问题:

  • bucket 特征可廉价复制
  • 存储桶的生命周期与特征的副本无关(因为不持有所有权)
  • bucket 数据自然不是 const,即使 trait 实例是

修复

在这里应用相同的方法:

Live On Coliru

#include <boost/intrusive/unordered_set.hpp>
#include <vector>
#include <iostream>

namespace BI = boost::intrusive;
struct ValuableData : public BI::unordered_set_base_hook<> {
    int  x = 0;
    int  y = 0;
    char c = 0;
};

using Options    = BI::base_hook<BI::unordered_set_base_hook<> >;
using BucketType = BI::unordered_bucket<Options>::type;
using BucketPtr  = BI::unordered_bucket_ptr<Options>::type;

struct VectorTraits {
    std::vector<BucketType>* v;
    BucketPtr bucket_begin() const { return v->data(); }
    [[nodiscard]] uint32_t bucket_count() const { return v->capacity(); }
};

struct dumhash {
    uint32_t operator()(const ValuableData& data) const { return data.x; }
};

struct dumcomp {
    bool operator()(const ValuableData& data, const ValuableData& datb) const {
        return data.x == datb.x;
    }
};
typedef BI::unordered_set<ValuableData, BI::hash<dumhash>, BI::equal<dumcomp>,
                          BI::bucket_traits<VectorTraits>>
    MySet;

int main() {
    std::vector<BucketType> buckets(13);
    VectorTraits tr { &buckets };

    MySet set(tr);
    set.rehash(tr);

    std::cout << buckets.size() << "\n";
    buckets.resize(57);
    set.rehash(tr);

    std::cout << buckets.size() << "\n";
}

打印

13
57

【讨论】:

  • 我们如何获得标记为 'const_bucket_ptr' 的类型?
  • @etienneparmentier 看起来这可能是文档疏忽:它可能以前存在,但也可能是从有关非指针类型的部分复制的。在实践中,所有指针都可以是普通的 const 而不改变指向的类型(突变来自可复制性)
猜你喜欢
  • 2012-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多