2017-04-11 90 views
2

考虑下面的代码:修改std :: set的元素 - 定义的行为?

#include <set> 

struct X { 
    int a, b; 
    friend bool operator<(X const& lhs, X const& rhs) { 
     return lhs.a < rhs.a; 
    } 
}; 

int main() { 
    std::set<X> xs; 
    // some insertion... 
    auto it = xs.find({0, 0}); // assume it != xs.end() 

    const_cast<X&>(*it).b = 4; // (1) 
} 

(1)定义的行为?也就是说,我允许const_cast提及从const_iterator获得的元素的引用,如果修改不会改变排序,那么可以修改它吗?

我已经阅读了一些帖子,在这里和那里提出这种const_cast,但我想知道这是否是实际定义的行为。

回答

2

这是否定义了行为尚不清楚,但我相信它确实存在。

std::set在修改值时似乎没有特别的禁止,除了你已经暗示的限制,比较器在传递相同的输入时必须返回相同的结果([associative.reqmts] p3) 。

但是,修改定义为const的对象的一般规则确实适用。是否set将其元素定义为const未被拼出。如果有,则不允许修改元素([dcl.type.cv] p4)。

但[container.requirements.general] P3读取:

对于申报allocator_type受此节的组件,存储在这些组件的对象应采用allocator_traits<allocator_type>::construct功能被构造和 使用破坏allocator_traits<allocator_type>::destroy函数(20.7.8.2)。

std::set<T>声明一个allocator_type,默认为std::allocator<T>std::allocator_traits<allocator_type>::construct将其传递给T *,导致构建了T,而不是const T

我认为这意味着std::set<T>不允许将其元素定义为const,并且由于缺少其他禁止,意味着允许通过const_cast修改元素。


个人而言,如果我找不到任何更好的选择,我会考虑通过把整个事情的包装结构,它定义了一个成员mutable X x;回避的问题。这可以在没有const_cast的情况下进行修改,只要您注意避免更改集合中已有元素的相对顺序即可。它还将作为您的代码的其他读者的文档,说明您的设置的元素可以并将被修改。