我有一个遍历对象树的函数,不会修改树中的任何对象。
该功能如下所示:
static Node* findMatchingNode(const Node& root, const SomeFilterData& d);
struct Node {
Node* left;
Node* right;
};
该函数可以返回树中的根或任何对象,也可以不返回任何对象。显而易见的是,我必须在某处执行 const_cast ,这在大多数情况下都是禁止的。
函数是否可以保证constness并同时允许任何人修改其输出?
修改即可。我没有明确说明,我的函数确实没有修改树中的任何节点,也没有创建新的Node,它是纯函数。我想在那里总是有const
限定符,告诉每个人该函数不会修改任何内容
修改即可。后面的未说出的问题是,在函数执行期间(并且仅在函数内部)没有合法的方式表达输入的常量而不强制执行输出常量。
答案 0 :(得分:7)
与往常一样,您无法修改const
数据,因为它是常量。特别是:
尽管const_cast可以从任何指针或引用中删除constness或volatile,但是使用结果指针或引用来写入声明为const的对象或访问声明为volatile的对象会调用未定义的行为。
(来自here)
因此,如果指向const
输入参数root的指针是一个明智的结果,那么返回类型也应该是const
。无论如何,你应该非常小心返回指针:截至目前,你的函数可以返回指向临时指针的指针!
最好返回boost::optional<Node>
或std::optional<Node>
(如果后者使用C ++ 17并使用该标准)。
如果您的函数仅对可修改的输入root
有意义(例如,如果结果必须可修改且,则结果可以是{{的地址1}}),在你的声明中删除root
。这也会阻止输入const
成为临时的。
最有可能解决您潜在的XY问题:
如果它适合您的用例(我发现它高度不太可能),最可能更好的选择是在可迭代的数据结构上定义您的算法,如树或list(无论你的root
是哪个节点),然后将迭代器返回到匹配节点,或者如果不存在这样的节点则返回过去的迭代器(相对于高级结构)。
至于Node
- 与 - 非 - const
讨论:使用迭代器选项,你可以区分高级结构的常量和你比较东西的参考节点 - 高级结构上的const
迭代器和引用输入节点的const
参数(现在暂时不会出现问题)。
从我的问题中可以看出,您的功能甚至可以用std::find_if
替换。那么你首先不会遇到这个问题。
答案 1 :(得分:6)
你的代码不是const-correct,它会删除const。这就是为什么你遇到“必需的”常量问题的原因。不要这样做:
static const Node* findMatchingNode(const Node& root, const SomeFilterData& d);
您可能会指出,您可能希望从 修改节点的其他函数调用此函数,因此需要非const结果。因此,这应该是一个具有完全不同签名的新功能:
static Node* findMatchingNode(Node& root, const SomeFilterData& d);
您可能会指出它们具有相同的主体,并且存在DRY原则(DRY =不重复自己=没有复制粘贴的代码)。是的,所以这里只需要一个快捷方式:const_cast。我认为在这种情况下它是正常的,因为只有目的是共享代码,并且很明显它没有违反任何const-correctness原则。
//This function does not modify anything
static Node* findMatchingNode(Node& root, const SomeFilterData& d) {
return const_cast<Node*>(findMatchingNode(const_cast<const Node&>(root),d));
}
Loki Asari建议添加第三个私有函数findMatchingNodeCommon()
,findMatchingNode()
的两个版本都会调用。然后你可以减少一个const_cast
。如果你想变得极端,那么你可以findMatchingNodeCommon()
模仿,然后所有const_cast
都会消失。我认为这不值得打扰,但他们是非常有效的意见,所以值得一提。
答案 2 :(得分:3)
引用返回和const成员函数之间的关系是什么?
如果要通过检查器方法引用返回此对象的成员,则应使用reference-to-const(const X&amp; inspect()const)或value(X inspect()const)返回它
所以是的,你应该通过值或const引用/指针返回。您没有使用this
,但模式是相同的。
答案 3 :(得分:0)
我看看STL是如何做到的。在那里,你有一个序列(由迭代器分隔),一个定义匹配的谓词和一个搜索的模板函数。由于它是一个模板函数,它会自动将返回值const限定符与参数的值匹配。
如果由于某种原因,您不想编写模板函数,您还可以添加两个重载,因为您可以使用不同的const限定符重载函数。
答案 4 :(得分:0)
我想回答自己,因为最初的问题形状不正确。所以,我的回答不能被视为正确的答案。
没有合法的方式来表达输入的常量(在函数执行期间)而不强迫用户不修改输出(并且不强制执行输出常量)。
没有_const_cast _的方式:
static Node* findMatchingNode(Node& root, const SomeFilterData& d);
看起来并不能保证输入树的常量。
原始函数声明允许这样的黑客攻击,这也是不合适的:
const Node& root = ...
Node* result = findMatchingNode(root, filter);
result->doBadNonConstThings();