具有可变成员的“std::p air”的类似物?

Analogue of `std::pair` with mutable members?

提问人:SherAndrei 提问时间:6/17/2023 最后编辑:SherAndrei 更新时间:6/18/2023 访问量:145

问:

我正在实现基于 的调用的类似物。想法很简单:当有新的插入物时,将检查过期的元素,如果存在,则将其删除。std::mapexpiring_mapboost::multi_index::multi_index_containerexpiring_map

我想有一些类似的接口:std::map

template <class Clock, class Key, class T, class Compare = std::less<Key>>
class expiring_map
{
    explicit expiring_map(clock_type cl, duration expiry_duration);

    void insert_or_assign(value_type v);

    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;

    // size(), clear() and lookup-methods are omitted for simplicity
};

我还希望下一个代码有效

    auto clock = std::make_shared<std::chrono::steady_clock>();
    expiring_map<decltype(clock), int, std::string> m{ clock, std::chrono::seconds{ 1 } };
    m.insert_or_assign({ 1, "value" });
    const auto b = m.begin();
    auto v = std::move(b->second);
    EXPECT_EQ(v, "value");
    EXPECT_TRUE(b->second.empty());

由于索引的所有元素都被认为是不可变的,因此(根据答案)我达到所需结果的唯一方法是指定下一个结构boost::multi_index::multi_index_containervalue_typeexpiring_map

struct value_type {
    const Key first;
    mutable T second;
};

因为 c++ 表达式无效,因为关键字是关于存储持续时间的,而不是关于类型的。从 https://en.cppreference.com/w/cpp/language/cvusing value_type = std::pair<const Key, mutable T>mutable

C++ 语言语法将可变视为存储类说明符,而不是类型限定符,但它不会影响存储类或链接。

问题

我对这个解决方案的问题是我的重载,而不是真正的常量,下一个代码正在编译:constbegin()end()

using subject_type = ::expiring_map<std::shared_ptr<std::chrono::steady_clock>, int, std::string>;

void foo(const subject_type& s) {
   s.begin()->second = "new";
}

我怎样才能改变我的实现,以实现const重载,并实现编译错误,但保留这些方法(以便仍然能够使用基于范围的迭代)?begin()end()const expiring_map&

链接 godbolt 与当前实现和测试

我试过什么

我尝试使用不同的提取器,例如

    struct extract_value_type_as_const_ref {
        [[nodiscard]] std::pair<const Key&, const T&> operator()(const node_type& n) const { return { n.second.first, n.second.second }; }
    };

    using const_iterator = decltype(boost::make_transform_iterator<extract_value_type_as_const_ref>(std::declval<underlying_type>().template get<by_value_type>().cbegin()));

但需要 for result of 可转换为,但我真的不想复制我的所有构造函数::testing::ElementsAre*(map.begin())value_typestd::pairvalue_type

C++(英语:C++) 提高 可变 性病对 boost-multi-index

评论

0赞 Eljay 6/18/2023
对于那些在家中使用 Macintosh 的人(而不是指向 Matt Godbolt 的在线编译器的链接),您可能需要 和 .brew install googletestclang++ -std=c++17 -lgtest -lgmock a.cpp
0赞 Eljay 6/18/2023
你能这样做吗:?const_iterator begin() const = delete;
0赞 SherAndrei 6/18/2023
@Eljay,不幸的是,没有。 需要 const 重载。我绝对可以没有,但是如果明天有人必须使用基于范围的for遍历我的容器,就会出现意外的编译错误::testing::ElementsAre::testing::ElementsAre
1赞 Eljay 6/18/2023
auto v = std::move(b->second);没有意义。 是 ,而不是指针。bstd::string
1赞 Eljay 6/18/2023
我不会试图让它隐含/默默地工作。对于 ,我会在 second 上添加一个 mutating 访问器,并强制调用站点将第二个值显式访问为 mutating。.这就把所有的炸药都放在了来电者的手中。(由你决定是否应该宣布,但我不这么认为。这里有龙。value_typeT& mut_second() const { auto& self = const_cast<value_type&>(*this); return self.second; }secondmutable

答:

0赞 Jan Schultke 6/18/2023 #1

您的成员不应是 .这是修复:secondvalue_typemutable

struct value_type {
    const Key first;
    T second;
};

进行此更改将使您在编译器资源管理器上的断言通过。

也许你对地图的总体误解了。 A 将存储 .value_typestd::map<Key, Value>value_type = std::pair<const Key, Value>

  • 必须是 .否则,我们将能够在不改变映射中对的位置的情况下更改键,这会破坏数据结构。Keyconst
  • 不可。A 将暴露 ,并且存储在该对中的 也是 。1)Valueconst std::mapconst value_type&Valueconst

此外,它不会编译是有道理的。 是数据成员(如 OR)的属性,它不是 A 类型的一部分。std::pair<const Key, mutable T>mutableprivatestatic

通常,仅用于在所有情况下都必须可变的数据成员,即使在对象内部也是如此。例如,成员通常是可变的,因为 a 不可用,无论如何您都需要能够使用它。mutableconststd::mutexconst std::mutex


1)里面存储的可能不是一个对象,而是一个可变的对象。从技术上讲,你仍然可以在那时候改变它。但是,这不能依赖。Valueconstconst_cast

评论

0赞 SherAndrei 6/18/2023
很抱歉给您带来不便,我已经更新了问题中的链接,添加了所需的测试,其中删除并不能挽救这种情况mutable
0赞 SherAndrei 6/18/2023
> mutable仅用于在所有情况下都必须可变的数据成员,是的,我知道,但我想找到一种方法来修改元素,仅使用我的迭代器,而无需辅助方法,例如boost::multi_index::multi_index_containermodifyreplaceboost