使用修改后的指向常量的指针返回指向非常量的指针

Return pointer-to-non-const using modified pointer-to-const

本文关键字:指针 常量 返回 非常 修改      更新时间:2023-10-16

从函数返回指向非const的指针的最佳做法是什么,其中该指针是通过将(非const)指针修改为-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_castconst,这似乎很好,但有更好的方法吗?

至少在标准库中,最佳实践是提供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被实现为一个展开树,find将不得不对其进行修改,但我认为由于复杂性保证,该标准不允许使用展开树。

[编辑:经过两次编辑,我认为我的答案和larsmans的答案现在是一样的,但事情的顺序不同,不同选项的详细程度也不同。我鼓励人们不要对这个答案投赞成票,除非你看到它和larsmans'之间有一些重要的区别。如果没有人找到,我会删除它。]

如果您确信*inputconst-ness应该而不是意味着该函数不会为您提供修改列表中其他节点的方法,那么修复方法是:

NODE *top_level(const NODE *input_)
{
  NODE *input = const_cast<NODE*>(input_);
  while (input->parent != nullptr)
    input = input->parent;
  return input;
}

但这对我来说似乎是错误的,,因为函数可以返回与中传递的指针值完全相同的指针值。

因此,由于它可以为其他代码(尽管是调用者)提供修改其输入的方法,因此其输入不应标记为const。"正确"的解决方案是提供常量和非常量重载,因为这就是避免编写"偷偷带走常量"的函数的方法,比如strstr的非重载C版本和其他返回非常量的字符串搜索函数。

如果不是因为从指向同一节点的指针中删除const的风险(假设输入在某种程度上被保证不是顶级节点,并且假设你仍然确信第一个节点的const不应该意味着列表中其他节点的const),那么你可以这样写:

NODE *top_level(const NODE *input)
{
  NODE *result = input->parent;
  while (result->parent != nullptr)
    result = result->parent;
  return result;
}

请注意,我们不再需要const_cast,当我们添加了使函数本身成为负责任的const安全公民所必需的假设时,它已经消失了。系统正常工作!;-)

通常的解决方案,至少在接口级别,是在const上重载,提供两个函数,NODE const* top_level( NODE const* input )NODE* top_level( NODE* input )。如果您不想重复代码,那么可以使用const_cast来实现其中一个。