我如何改进这个迫使我声明成员函数 const 并声明变量可变的设计?

发布于 2024-12-17 17:29:06 字数 883 浏览 0 评论 0原文

由于某种原因,我正在迭代 std::set 中的类的元素,并且希望稍微修改键,因为我知道顺序将保持不变。

std::set 上的迭代器是 const_iterators,因为如果修改键,可能会导致顺序错误,从而导致集合损坏。但是我确信我的操作不会改变集合中元素的顺序。

目前,这是我的解决方案:

class Foo
{
public:
    Foo(int a, int b): a_(a),b_(b) {}
   ~Foo(){}
    bool operator < (const Foo& o) const { return this.a_ < o.a_ ; }
    void incrementB() const { ++b_; } // <-- the problem: it is not const!
private:
    const int a_;
    mutable int b_;                   // <-- I would like to avoid this
}

void f()
{
    std::set<Foo> s;
    // loop and insert many (distinct on a_) Foo elements;
    std::for_each(s.begin(), c.end(), [](const Foo& s) { s.incrementB(); }); // Foo must be const. iterators are const_iterators
}

您将如何修改它(我知道我可以使用 std::map 但我很好奇您是否可以建议其他选项)以删除 mutable 和 const?

谢谢

For some reason I am iterating over elements of a class in an std::set and would like to slightly modify the keys, knowing that the order will be unchanged.

Iterators on std::set are const_iterators because if the key is modified, it might result in a bad order and therefore in set corruption. However I know for sure that my operations won't change the order of my elements in the set.

For the moment, here is my solution:

class Foo
{
public:
    Foo(int a, int b): a_(a),b_(b) {}
   ~Foo(){}
    bool operator < (const Foo& o) const { return this.a_ < o.a_ ; }
    void incrementB() const { ++b_; } // <-- the problem: it is not const!
private:
    const int a_;
    mutable int b_;                   // <-- I would like to avoid this
}

void f()
{
    std::set<Foo> s;
    // loop and insert many (distinct on a_) Foo elements;
    std::for_each(s.begin(), c.end(), [](const Foo& s) { s.incrementB(); }); // Foo must be const. iterators are const_iterators
}

How would you modify it (I know I could use an std::map but I am curious whether you can suggest other options) to remove mutable and const?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

无所谓啦 2024-12-24 17:29:06

你不能。为了容器的正确性,集合元素必须是 const:

它迫使您认识到关键部分需要是不可变的,否则数据结构不变量将被破坏。

struct element 
{
     std::string key_part; // const in the set

     bool operator<(const element&o) const { return key_part<o.key_part; }

  private:
     mutable int m_cached; // non-key, *NOT* used in operator<
};

如果您想保留在非关键部分中“表达”常量性的可能性,请将其分成对并将它们存储在映射中:

std::map<std::string /*key_part*/, int /*m_cached*/> mapped;

或者,更灵活:

struct element 
{
     std::string key_part; // const in the set

     bool operator<(const element&o) const { return key_part<o.key_part; }

     struct value {
         int m_cached;
         int m_moredata; //...
     } /*not in the element itself*/;
};

std::map<element, element::value> mapped;

You can't. Set elements are required to be const for container correctness:

It forces you to realize that the key part needs to be immutable, or the data structure invariants would be broken.

struct element 
{
     std::string key_part; // const in the set

     bool operator<(const element&o) const { return key_part<o.key_part; }

  private:
     mutable int m_cached; // non-key, *NOT* used in operator<
};

If you wanted to retain the possibility to 'express' const-ness in the non-key part, split it out into pairs and store them in a map:

std::map<std::string /*key_part*/, int /*m_cached*/> mapped;

or, more flexibly:

struct element 
{
     std::string key_part; // const in the set

     bool operator<(const element&o) const { return key_part<o.key_part; }

     struct value {
         int m_cached;
         int m_moredata; //...
     } /*not in the element itself*/;
};

std::map<element, element::value> mapped;
北城挽邺 2024-12-24 17:29:06

另一种选择是将 const_cast 转换为引用类型:

class Foo
{
public:
    void incrementB() const { ++ const_cast< int& >( b_ ); }
private:
    int b_;
};

但正如 sehe 已经说过的,您不应该修改 set 的元素。

Another option is to const_cast to a reference type :

class Foo
{
public:
    void incrementB() const { ++ const_cast< int& >( b_ ); }
private:
    int b_;
};

But as sehe already said, you shouldn't modify set's elements.

浅暮の光 2024-12-24 17:29:06

一种可能是在 pimpl 中分解 Foo 的值部分。

class Element
{
public:

    Element(int key, int value);

    Element( const Element& el );
    Element( Element&& el );

    ~Element();

    bool operator < (const Element& o) const;

    void incrementValue() const;
    int  getValue() const;

private:

    Element& operator=(const Element& );
    Element& operator=( Element&& el );

    struct Key
    {
        Key( const int key ) : m_KeyValue( key )
        {
        };

        const int m_KeyValue;
    };

    struct Value;

    const   Key                 m_Key;
    std::unique_ptr<Value>      m_Value;

};

struct Element::Value
{
    Value( int val ) : value(val)
    {

    }

    int value;
};

Element::Element(int key, int value) : 
    m_Key(key),
    m_Value( new Element::Value(value) )
{

}

Element::~Element()
{

}

Element::Element( const Element& el ) : 
    m_Key( el.m_Key ),
    m_Value( new Element::Value( *el.m_Value ) )
{

}

Element::Element( Element&& el ) : 
    m_Key(el.m_Key)
{
    m_Value = std::move(el.m_Value);
    el.m_Value.release();
}

bool Element::operator < (const Element& o) const 
{ 
    return m_Key.m_KeyValue < o.m_Key.m_KeyValue; 
}

void Element::incrementValue() const
{
    m_Value->value++;
}

int  
Element::getValue() const
{
    return m_Value->value;
}

void f()
{
    std::set<Element> s;

    s.insert(Element(1,2));
    s.insert(Element(2,3));

    std::for_each(s.begin(), s.end(), [](const Element& s) { s.incrementValue(); });

    std::for_each(s.begin(), s.end(), [](const Element& s) 
    { 
        std::cout << s.getValue() << std::endl; 

    }); 
}

int 
main()
{
    f();
    return 0;
}

编辑:但是说实话,您必须决定额外的间接级别是否有意义,否则您最好使用地图。

One possibility might be to factor out the value part of Foo in a pimpl.

class Element
{
public:

    Element(int key, int value);

    Element( const Element& el );
    Element( Element&& el );

    ~Element();

    bool operator < (const Element& o) const;

    void incrementValue() const;
    int  getValue() const;

private:

    Element& operator=(const Element& );
    Element& operator=( Element&& el );

    struct Key
    {
        Key( const int key ) : m_KeyValue( key )
        {
        };

        const int m_KeyValue;
    };

    struct Value;

    const   Key                 m_Key;
    std::unique_ptr<Value>      m_Value;

};

struct Element::Value
{
    Value( int val ) : value(val)
    {

    }

    int value;
};

Element::Element(int key, int value) : 
    m_Key(key),
    m_Value( new Element::Value(value) )
{

}

Element::~Element()
{

}

Element::Element( const Element& el ) : 
    m_Key( el.m_Key ),
    m_Value( new Element::Value( *el.m_Value ) )
{

}

Element::Element( Element&& el ) : 
    m_Key(el.m_Key)
{
    m_Value = std::move(el.m_Value);
    el.m_Value.release();
}

bool Element::operator < (const Element& o) const 
{ 
    return m_Key.m_KeyValue < o.m_Key.m_KeyValue; 
}

void Element::incrementValue() const
{
    m_Value->value++;
}

int  
Element::getValue() const
{
    return m_Value->value;
}

void f()
{
    std::set<Element> s;

    s.insert(Element(1,2));
    s.insert(Element(2,3));

    std::for_each(s.begin(), s.end(), [](const Element& s) { s.incrementValue(); });

    std::for_each(s.begin(), s.end(), [](const Element& s) 
    { 
        std::cout << s.getValue() << std::endl; 

    }); 
}

int 
main()
{
    f();
    return 0;
}

EDIT: To be honest however you must decide if the extra level of indirection makes sense or you would be better off using a map.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文