在C ++中的Const-correctness语义 [英] Const-correctness semantics in C++

查看:135
本文介绍了在C ++中的Const-correctness语义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于有趣和利润™,我在C ++中编写一个trie类(使用C ++ 11标准。)

For fun and profit™, I'm writing a trie class in C++ (using the C++11 standard.)

trie T 有一个迭代器, trie< T> :: iterator 。 (它们实际上都是 const_iterator ,因为你不能修改trie的 value_type 。)迭代器的类声明看起来部分如下:

My trie<T> has an iterator, trie<T>::iterator. (They're all actually functionally const_iterators, because you cannot modify a trie's value_type.) The iterator's class declaration looks partially like this:

template<typename T>
class trie<T>::iterator : public std::iterator<std::bidirectional_iterator_tag, T> {
    friend class trie<T>;
    struct state {
        state(const trie<T>* const node, const typename std::vector<std::pair<typename T::value_type, std::unique_ptr<trie<T>>>>::const_iterator& node_map_it ) :
            node{node}, node_map_it{node_map_it} {}
// This pointer is to const data:
        const trie<T>* node;
        typename std::vector<std::pair<typename T::value_type, std::unique_ptr<trie<T>>>>::const_iterator node_map_it;
    };
public:
    typedef const T value_type;
    iterator() =default;
    iterator(const trie<T>* node) {
        parents.emplace(node, node->children.cbegin());
            // ...
    }
    // ...
private:
    std::stack<state> parents;
    // ...
};

请注意节点 code> const 。这是因为(在我看来)迭代器不应该修改它指向的节点;它只是一个迭代器。

Notice that the node pointer is declared const. This is because (in my mind) the iterator should not be modifying the node that it points to; it is just an iterator.

现在,在我的主 trie< T> 函数,它有一个共同的STL签名 - 它需要一个迭代器来清除数据(并返回迭代器下一个对象)。

Now, elsewhere in my main trie<T> class, I have an erase function that has a common STL signature--it takes an iterator to data to erase (and returns an iterator to the next object).

template<typename T>
typename trie<T>::iterator trie<T>::erase(const_iterator it)
{
    // ...

    // Cannot modify a const object!
    it.parents.top().node->is_leaf = false;

    // ...
}

因为节点指针是只读的! erase 函数肯定应该修改迭代器指向的trie,即使迭代器不应该这样。

The compiler complains because the node pointer is read-only! The erase function definitely should modify the trie that the iterator points to, even though the iterator shouldn't.

因此,我有两个问题:


  1. 应该 iterator 的构造函数 c>和必须具有 c end()成员,当然 trie< T> :: iterator trie< T> 是共同的朋友,但我不知道什么约定是。将它们设为private可以解决我从迭代器构造函数中删除 const promise的麻烦。

  2. 有关迭代器及其节点的正确 const 语义/约定是什么?没有人向我解释过这个,我在网上找不到任何教程或文章。这可能是更重要的问题,但它需要大量的规划和适当的实施。

  1. Should iterator's constructors be public? trie<T> has the necessary begin() and end() members, and of course trie<T>::iterator and trie<T> are mutual friends, but I don't know what the convention is. Making them private would solve a lot of the angst I'm having about removing the const "promise" from the iterator's constructor.
  2. What are the correct const semantics/conventions regarding the iterator and its node pointer here? Nobody has ever explained this to me, and I can't find any tutorials or articles on the Web. This is probably the more important question, but it does require a good deal of planning and proper implementation. I suppose it could be circumvented by just implementing 1, but it's the principle of the thing!


推荐答案

1)所有迭代器都需要是可复制构造的。您的迭代器是双向的,因此也必须是默认可构造的( http:// en.cppreference.com/w/cpp/concept/ForwardIterator ),虽然我不知道为什么。所以默认的构造函数需要是public的,但你可以做你喜欢的 const trie< T> * 一。我认为它应该是私有的,因为这个类的目的是为用户提供一个迭代器的trie,所以它的公共接口应该只是一个适当类别的迭代器。不需要任何额外的公共构造函数。

1) All iterators are required to be copy-constructible. Your iterator is Bi-directional, hence is also required to be default-constructible (http://en.cppreference.com/w/cpp/concept/ForwardIterator), although I don't know why. So the default constructor needs to be public but you can do what you like with the const trie<T>* one. I would think it should be private, since the purpose of this class is to provide the user with an iterator over the trie, and so its public interface should be only that of an iterator of the appropriate category. No need for any extra public constructors.

2) erase 是一个非const函数。你可以有效地传递给它的唯一迭代器是引用该函数所调用的同一个特里提出的迭代器,这意味着(我认为,虽然我不确定我遵循你的设计)父级的整个层次结构非const对象。所以我怀疑这是你可以 const_cast< trie< T> *(it.parents.top()。node)的情况之一。不允许使用迭代器来修改trie,这就是为什么你希望它保存一个指向const的指针。但是当你持有一个非const指针到trie,即 this ,你可以修改它的任何部分,迭代器只是给你的位置开始修改。

2) erase is a non-const function. The only iterators you can validly pass to it are iterators that refer to the same trie that the function is called on, which means (I think, although I'm not quite certain I've followed your design) the whole hierarchy of parents are non-const objects. So I suspect this is one of those cases where you can const_cast<trie<T>*>(it.parents.top().node). The iterator isn't allowed to use it to modify the trie, which is why you want it to hold a pointer-to-const. But when you hold a non-const pointer to the trie, namely this, you are allowed to modify any part of it you like, and the iterator is just giving you the position to start modifying from.

这里可以有一些更为通用的const-safety原则。在 container :: erase(const_iterator)函数中的一种可能情况是 const container * 迭代器等于 this 。在这种情况下, const_cast 肯定是安全和合法的(以及不必要的,因为你可以使用 this 但这是不是它的const正确的点)。在你的容器中,它不等于 this ,它指向几个 trie 对象之一一起构成这个是其一部分的分层容器。好消息是,整个容器在逻辑上是const或逻辑上非常量在一起,因此 const_cast 是安全和合法的,如果它是所有一个对象。但有点难以证明是正确的,因为你必须确保在你的设计中,整个分层容器真正地,正如我假设的,共享非常量。

There might be some more general principle of const-safety you can draw here. One possible case in container::erase(const_iterator) functions is that the const container* you'd get from the iterator is equal to this. In that case the const_cast is certainly both safe and legitimate (as well as unnecessary, because you can just use this, but that's beside the point of whether it's const-correct or not). In your container it is not (in general) equal to this, it points to one of the several trie objects that together make up the hierarchical container that this is part of. The good news is, that whole container is logically const or logically non-const together, hence the const_cast is just as safe and legitimate as if it were all one object. But a bit harder to prove correct, because you have to make sure that in your design the whole hierarchical container genuinely does, as I've assumed, share non-constness.

这篇关于在C ++中的Const-correctness语义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆