从函数返回指针到非const
的最佳做法是什么,其中指针是通过修改(非const
)指针到{{1}获得的}?像这样:
const
我可以NODE *top_level(const NODE *input)
{
while (input->parent != nullptr)
input = input->parent; // NODE::parent is (non-const) NODE*
return input; // Compile failure:
// Cannot convert from 'const NODE *' to 'NODE *'
}
返回const_cast
,这看起来不错,但还有更好的方法吗?
答案 0 :(得分:6)
至少在标准库中,最佳做法是提供const
和非const
重载。例如。 std::strchr
在<cstring>
中声明为
char *strchr(char *s, int c);
char const *strchr(char const *s, int c);
类似地,像std::map<T>::find
这样的函数有重载,例如
iterator find(const Key& key);
const_iterator find(const Key& key) const;
请注意,第一个版本没有const
限定符,即使find
本身没有理由修改map
。(*)关键是你得到了一些东西可以用于修改地图的find
,因此通过一种“可变性的传递性”,find
不能是const
。我认为同样的情况适用于你的问题。
或者,您可以使用const_cast
,但对我来说,这就像打破了承诺。
关于这种情况的有趣之处在于,如果你可以保证你的函数永远不会被调用到树的顶部项目(或输入的任何东西),那么就不需要强制转换或重载:
struct node {
node *parent;
};
node *top(node const *n)
{
node *p = n->parent;
while (p->parent != 0)
p = p->parent;
return p;
}
编译时没有任何警告。
(*)如果std::map
被实现为一个splay树,find
将不得不修改它,但我不认为标准允许splay树,因为复杂性保证。
答案 1 :(得分:2)
[编辑:在我们的两次编辑之后,我认为我的答案和larsmans'现在是相同的,但是不同的顺序和不同选项的细节水平不同。我鼓励人们不要打扰这个答案,除非你看到它和larsmans之间有一些重要的区别。如果没有人找到,我会删除它。]
如果您确定const
的{{1}} - 要修改列表中的其他节点,那么修复就是:
*input
但这对我来说似乎不对,因为该函数可以返回与中传递的完全相同的指针值。
因此,由于它可以为其他代码(尽管是调用者)提供修改其输入的方法,因此不应将其输入标记为NODE *top_level(const NODE *input_)
{
NODE *input = const_cast<NODE*>(input_);
while (input->parent != nullptr)
input = input->parent;
return input;
}
。 “正确”的解决方案是提供const和非const重载,因为这就是你如何避免编写“走私constness”的函数,比如const
的非重载C版本和其他返回非的字符串搜索函数-const。
如果不是因为从指向同一节点的指针中删除const的风险(想象一下输入在某种程度上保证不是顶级节点,并且想象你仍然确定它的常量第一个节点不应该暗示列表中其他节点的常量),那么你可以这样写:
strstr
请注意,我们不再需要NODE *top_level(const NODE *input)
{
NODE *result = input->parent;
while (result->parent != nullptr)
result = result->parent;
return result;
}
,当我们添加必要的假设以使函数本身成为负责任的安全公民时,它已经消失了。系统有效! ; - )
答案 2 :(得分:0)
通常的解决方案,至少在接口级别,是const
上的重载,提供两个函数,NODE const* top_level( NODE const* input )
和NODE* top_level( NODE* input )
。如果您不想复制代码,则可以使用const_cast
来实现另一个代码。