简体   繁体   中英

shared_from_this() returns std::shared_ptr<const X>, not std::shared_ptr<X>

OK, this one has me stumped. Clearly I've missed something, so I hope someone can tell me what it is.

I'm developing a C++17 library. I've written a custom tree data structure composed of Node objects and a custom iterator, Node::iterator , for traversing the tree. The iterator looks like this:

template <typename T>
class NodeIterator {
public:
    using value_type = T;
    using difference_type = std::ptrdiff_t;
    using pointer = std::shared_ptr<T>;
    using reference = T&;
    using iterator_category = std::forward_iterator_tag;

    NodeIterator() = default;
    NodeIterator(pointer n);

    // Etc.
}

And later...

template class NodeIterator<Node>;
template class NodeIterator<const Node>;

When I add the standard iterator methods ( begin() , end() , and the const equivalents) to a parent class Tree , I can control the initial value for the iterator. So I can say

Node::iterator Tree::begin() const {
    return Node::iterator(_root);
}

where _root is a std::shared_ptr<Node> . This works great.

However, not content to leave well enough alone, I want these iterator methods on the node itself. That way I can traverse a subtree from any node, eliminate the Tree class altogether, and just pass around Node objects.

I declare Node as

class Node : public std::enable_shared_from_this<Node> {
public:
    using iterator = NodeIterator<Node>;
    using const_iterator = NodeIterator<const Node>;

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

    // Etc.
}

and define the iterator methods as

Node::iterator Node::begin() const {
    return Node::iterator(this->shared_from_this());
}

Node::iterator Node::end() const {
    return Node::iterator(nullptr);
}

Node::const_iterator Node::cbegin() const {
    return Node::const_iterator(this->shared_from_this());
}

Node::const_iterator Node::cend() const {
    return Node::const_iterator(nullptr);
}

The compiler then loudly complains about the return statement:

src/node.cc:79:9: error: no matching conversion for functional-style cast from
      'shared_ptr<const Node>' to 'Node::iterator' (aka 'NodeIterator<Node>')
        return Node::iterator(this->shared_from_this());
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

and later...

include/example.h:344:2: note: candidate constructor not viable: no known
      conversion from 'shared_ptr<const Node>' to 'shared_ptr<Node>' for
      1st argument
        NodeIterator(pointer n);
        ^

In another method, Node::appendChild() , I automatically set the parent node ( std::shared_ptr<Node> ) to this->shared_from_this() , and it works fine.

If I comment out Node::begin() and Node::end() and only use cbegin() and cend() in my loop, it also works fine.

What gives?

shared_from_this has const and non-const overloads. See cppreference . Within your const begin , this is pointer to const and calls the const overload which returns a shared_ptr to const.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM