我有一个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);
(...)
};
现在问题是,如果我调用函数Graph::get_node_by_id()
,我想返回一个指向给定节点的指针(类型Node
)。但似乎不可能这样做,因为std::set
隐式地将我的Node类型对象转换为const Node
个对象,而我无法从non-const pointer
对象中获取const
。
但是,我不能将其他所有内容设置为const Node
(这会解决问题),因为我想从Node::add_neighbour()
调用Graph::add_edge()
,但每当我这样做时,我的编译器虽然我将const
定义为只关心node_list
,但我可能会违反less operator<
集合中id
ness(需要有一个排序集)的元素。 1}}。
我能做些什么来解决这个难题(不放弃有排序的集合)?感谢您的回复!
有关错误的更多信息:
如果我使用非常量字段,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;
如果我使用常量字段,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);
答案 0 :(得分:3)
您的问题似乎是您有两种不同的价值语义&#39;到Node
。
一个是operator<
公开的,不受add_neighbour
影响。这是一个set
需求,通过制作Node
const
来保持秩序,并强制执行。
另一个是由类API公开的,其中set_id
和add_neighbour
都会更改值。
要保留已排序的set
,您必须不允许节点的ID在集合中更改。但你可以允许邻居改变。
因此,我建议您制作neighbours
set
mutable
,制作add_neighbour
private
和const
,然后制作{ {1}} Graph
friend
。
这是Node
为您提供的数据成员不属于&#39;值&#39;一种类型。请注意,这表示您指示持有mutable
的内容可能会使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);
(...)
};
Node
Graph
set
个Graph
个实例的公共,非const变异器,以及Node
的额外变异器{1}}用于更改set
中Graph
的邻居。
所以只有const Node b;
b.add_neighbour(nullptr);
可以做
Graph
如果您真的不信任private
,则可以将const
add_neighbour
class
替换为内部static add_neighbour(Node* node, Node* neighbour
,并class
1}}方法,因为内部class NeighbourHelper {
friend class Graph;
static void add(const Node* node, Node* neighbour) {
node->add_neighbour(neighbour);
}
隐式地能够访问外部类的私有数据。
Graph
现在只有const Node b;
Node::NeighbourHelper::add(&b, nullptr);
才能
Node a;
a.add_neighbour(nullptr);
在这两种情况下,以下内容适用于所有人:
public
此时,您应该感受到代码味道......问题是图表中的get_node_by_id
Node*
方法。实际上你可能想要公开某种迭代器而不是原始的Node
,并使Node
成为Graph的私有内部类。
甚至只需将整个std::map<int,std::set<int>>
概念替换为<tr ng-repeat="languages in samln">
<td>
<span>{{languages.emplang}}</span>
</td>
<td>
<input type="checkbox" name="checkbox1-" id="sp1" value="false" style="margin-left:40px;" ng-model="languages.speak">
</td>
<td>
<input type="checkbox" name="checkbox2-" id="rea1" value="false" style="margin-left:40px;" ng-model="languages.read">
</td>
<td>
<input type="checkbox" name="checkbox3-" id= "wr1" value="false" style="margin-left:40px;" ng-model="languages.write">
</td>
</tr>
...
但这取决于您的实际使用情况。
答案 1 :(得分:1)
虽然TBBle的分析是正确的,但有一个更简单的解决方案:将图std::set<Node>
替换为std::map<int,Node>
。
您当前的Graph::get_node_by_id()
正在使用线性搜索,因为set
并未真正提供您想要的查找。使密钥外部允许您删除operator<
重载并仍然可以获得更快,更自然的查找:map.find(id)
。
唯一丑陋的部分是现在你的Node
有一个必须与外键匹配的内部ID。如果你从不使用id除了在地图中查找节点,你可以完全删除它。如果你需要遵循图形边(邻居)然后检查ID,你可以用一组map迭代器替换你的指针集,如:
typedef std::map<int, Node> NodeMap;
typedef std::set<NodeMap::iterator> NeighbourMap;
然后您的遍历有pair<const int,Node>
可用。
NB。在反射中,从set更改为map会产生与TBBle的答案几乎相同的区别:将Node拆分为const和可变部分。使用此解决方案查找更清晰(您可以通过构造假节点作为set::find
的键来重新获得对数时间查找,但它仍然有点不优雅),并且对象标识与其他解决方案稍微更清晰。