简体   繁体   中英

Working with constant and non-constant functions - C++

I have 3 classes. In it's simplest form, it looks like,

class tree
{
public:
    tree_node* find_node(const std::string& text) {
       return factory.find(text);
    }
private:
    tree_node_factory factory;
}

class tree_node
{
public:
    tree_node(const std::string& text) : text_(text) {}

    const std::string& text() const {
       return text_;
    }

    void set_parent(const tree_node* new_parent);

private:
    std::string text_;
}

class tree_node_factory
{
public:
    tree_node* find(const std::string& text);
private:
    std::vector<tree_node*> allocated_nodes;
}

I don't want to allow users of tree to modify the tree_node returned by methods like find_node . So I changed, find_node and tree_node_factory::find to,

const tree_node* find_node(const std::string& text) const {
    return factory.find(text);
}

const tree_node* find(const std::string& text) const;

Problem is tree internally should be able to modify the nodes and work on methods like set_parent . But since factory returns only const nodes, I ended up with adding another overload (non const version) of find into the factory.

tree_node* find(const std::string& text);

I am wondering is this the correct way to handle these kind of problems? I see the code is getting duplicated in the const and non-const versions.

Any thoughts..?

Item 3 in Scott Meyers' book Effective C++ demonstrates a method to remove this code duplication. Basically, in your non-const function you will add const to this , call the const version, then cast the const away. This is safe; though writing to a const-variable leads to undefined behavior, because this was originally non-const it's okay.

Example:

const std::string& operator[](size_t index) const
{
    // some other code

    // since `this` isn't really const, this is modifiable 
    return mData[index]; 
}

std::string& operator[](size_t index)
{
    return const_cast<std::string&> // (3) take const off result
            (static_cast<const my_type&> // (1) add const
            (*this)[index]); // (2) use const version

}

Normally it would all be on one line. You can also make a utility for it.

Note a caveat: if the const version returns a "real" const object, this method clearly results in undefined behavior. The constness of the return value must be reflected by the constness of the object referred to by this . This is broken code:

const std::string& operator[](size_t index) const
{
    static const std::string constString = "Don't modify me.";

    if (index == 0)
    {
        // even though `this` isn't really const, this is NOT modifiable
        return constString; 
    }

    return mData[index - 1];
}

std::string& operator[](size_t index)
{
    return const_cast<std::string&> // (3) !!! take const off result !!!
            (static_cast<const my_type&> // (1)
            (*this)[index]); // (2)

}

In practice, we avoid global state so this is rarely an issue. It's trivial to check, anyway.

Unfortunately C++ has no tools (besides macros) for eliminating source code duplication in function overloads that look largely identical but differ in constness. You can, however, implement one of the functions using the other and const_cast .

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