const输入与非const输出

时间:2015-05-15 00:41:39

标签: c++

我有一个遍历对象树的函数,不会修改树中的任何对象。

该功能如下所示:

static Node* findMatchingNode(const Node& root, const SomeFilterData& d);

struct Node {
   Node* left;
   Node* right;
};

该函数可以返回树中的根或任何对象,也可以不返回任何对象。显而易见的是,我必须在某处执行 const_cast ,这在大多数情况下都是禁止的。

函数是否可以保证constness并同时允许任何人修改其输出?

修改即可。我没有明确说明,我的函数确实没有修改树中的任何节点,也没有创建新的Node,它是纯函数。我想在那里总是有const限定符,告诉每个人该函数不会修改任何内容

修改即可。后面的未说出的问题是,在函数执行期间(并且仅在函数内部)没有合法的方式表达输入的常量而不强制执行输出常量。

5 个答案:

答案 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)

根据Standard C++ Foundation

  

引用返回和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();