如何进行常量校正?

Ada*_*adi 9 c++ const const-correctness stdset

我有一个const-correctness问题,我似乎无法解决.这是我的程序的结构:

class Node
{
    private:
        int            id;
        std::set<Node*> neighbours;
    public:
        Node();
        Node(int id_p);

        void set_id(const int& id_p);
        int  get_id() const;
        void add_neighbour(Node* neighbour);
        bool is_neighbour(Node* neighbour) const;

        friend bool operator <(const Node& lhs, const Node& rhs);
};

class Graph
{
    private:
        std::set<Node> node_list;
    public:
        Graph();

        void        add_node(int id);
        const Node* get_node_by_id(int id) const;
        bool        has_node(int id) const;
        void        check_add_node(int id);
        void        add_edge(int id_1, int id_2);
        bool        has_edge(int id_1, int id_2) const;
        void        check_add_edge(int id_1, int id_2);

        (...)
};
Run Code Online (Sandbox Code Playgroud)

现在问题是,如果我调用该函数Graph::get_node_by_id(),我想返回一个指向给定节点(类型Node)的指针.但似乎不可能这样做,因为std::set隐式地将我的Node类型对象转换为const Node对象,并且我无法non-const pointerconst对象中获取a .

但是,我不能拥有一切设置为const Node(这将解决这个问题),因为我想打电话Node::add_neighbour()Graph::add_edge(),但每当我这样做,我的编译器说,我可能会违反const的内斯(有一个有序集合所需)集合中的元素node_list,即使我定义了less operator<只关心id.

我能做些什么来解决这个难题(不放弃有一个排序集)?谢谢您的反馈!

有关错误的更多信息:

如果我使用非常量字段,则错误Graph::get_node_by_id():

for(Node& element : this->node_list) // Error: element should be const Node&
{
    if(element->get_id() == id)
    {
        return element;
    }
}
return nullptr;
Run Code Online (Sandbox Code Playgroud)

如果我使用常量字段,则错误Graph::add_edge():

(...)
const Node* node_1 = this->get_node_by_id(id_1);
const Node* node_2 = this->get_node_by_id(id_2);
node_1->add_neighbour(node_2); // Error for disregarding constness
node_2->add_neighbour(node_1);
Run Code Online (Sandbox Code Playgroud)

TBB*_*Ble 3

您的问题似乎是您有两种不同的“值语义” Node

一种是暴露的,operator<不受 影响add_neighbour。这是set保持事物有序的需要,并且它通过 make 来强制执行Node const

另一个是由类 API 公开的,其中set_idadd_neighbour都会更改值。

为了保持排序set,一旦节点进入集合,就不能允许节点的 id 发生更改。但你可以允许邻居改变。

所以我建议你制作 the neighbours set mutable、制作add_neighbour privateand const、制作Grapha friendof Node

这就是为mutable您提供不属于类型“值”一部分的数据成员的原因。请注意,这意味着您表明持有 a 的内容const Node*可能期望结果在is_neighbour调用之间发生变化。

所以...

class Node
{
    private:
        // Trust Graph not to mess directly with these!
        int            id;
        mutable std::set<Node*> neighbours;

        friend class Graph;
        // For Graph's exclusive use
        void add_neighbour(Node* neighbour) const;


    public:
        Node();
        Node(int id_p);

        void set_id(const int& id_p); // Callable when not in Graph's set
        int  get_id() const;
        void add_neighbour(Node* neighbour);  // Callable when not in Graph's set
        bool is_neighbour(Node* neighbour) const;

        friend bool operator <(const Node& lhs, const Node& rhs);
};

class Graph
{
    private:
        std::set<Node> node_list;
    public:
        Graph();

        void        add_node(int id);
        const Node* get_node_by_id(int id) const;
        bool        has_node(int id) const;
        void        check_add_node(int id);
        void        add_edge(int id_1, int id_2);
        bool        has_edge(int id_1, int id_2) const;
        void        check_add_edge(int id_1, int id_2);

        (...)
};
Run Code Online (Sandbox Code Playgroud)

现在您拥有的是公共的非常量变元,用于Node不在Graphs中的实例set,以及一个额外的变元,用于更改其 s 中的Graph邻居。Nodeset

所以只能Graph

const Node b;
b.add_neighbour(nullptr);
Run Code Online (Sandbox Code Playgroud)

如果您确实不信任Graph,则可以将 替换private const add_neighbour为内部class, 方法static add_neighbour(Node* node, Node* neighbour,因为内部class可以隐式访问外部类的私有数据。

class NeighbourHelper {
    friend class Graph;
    static void add(const Node* node, Node* neighbour) {
        node->add_neighbour(neighbour);
    }
Run Code Online (Sandbox Code Playgroud)

现在只能Graph

const Node b;
Node::NeighbourHelper::add(&b, nullptr);
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,以下方法对每个人都适用:

Node a;
a.add_neighbour(nullptr);
Run Code Online (Sandbox Code Playgroud)

此时,您应该会感到代码味......问题在于public get_node_by_idGraph 中的方法。实际上,您可能想公开某种迭代器,而不是 raw Node*,并创建NodeGraph 的私有内部类。

或者甚至只是将整个Node概念替换为std::map<int,std::set<int>>......

但这取决于您的实际用例。