在const成员函数中调用非const成员函数

21
我想知道在const成员函数中是否可以调用非const成员函数。在下面的示例中,First会产生编译错误。我理解为什么会出错,我想知道是否有办法解决这个问题。
class Foo
{
   const int& First() const
   {
         return Second();
   }

   int& Second()
   {
        return m_bar;
   }

   int m_bar;
}

我不太想讨论这样做的智慧性,我只是好奇它是否可能。

3
你不是第一个看到这个问题的人:https://dev59.com/MHRA5IYBdhLWcg3wtwSe - Till Theis
谢谢Till,这在我的搜索中没有出现。 - Steve
7个回答

35
return (const_cast<Foo*>(this))->Second();

然后默默地哭泣。


如果这些函数有相同的名称怎么办? const int& GetBar() const; int& GetBar(); - phandinhlan
你可以在constness上重载一个函数,这样它就能正常工作。 - user3063349

11

可能

const int& First() const 
{ 
    return const_cast<Foo*>(this)->Second(); 
}

int& Second() { return m_bar; }

我不会推荐这样做,它很丑陋并且很危险(使用const_cast是危险的)。

最好将尽可能多的常见功能移入到辅助函数中,然后让您的常量和非常量成员函数只做它们需要做的工作。

在像这样的简单访问器的情况下,从两个函数中都return m_bar;就像从一个函数调用另一个函数一样容易。


或者只需将 int& Second() 改为 int& Second() const。非 const 成员函数调用 Second() 不会有任何问题,但现在 const 成员函数也可以直接调用它了,无需任何花招。 - Jeremy W. Sherman
@Jeremy:那么你认为我们该如何返回一个非const成员的引用呢? - GManNickG
@GMan:说得好。如果不将m_bar声明为mutable int,就无法实现。@Fred Larson的建议非常好,但它所引导的方向——每个访问器都要写两次,一次是带有const修饰符的,一次是没有的——并不是那么令人愉快。 - Jeremy W. Sherman
@Jeremy:在大多数情况下(不是所有情况,但大多数情况),不需要非const访问器(至少这是我的经验)。即使您确实需要两者,由于您必须编写两个函数,因此在两个函数中编写“return m_bar;”通常不会增加太多负担。 - James McNellis
问题是在 zip 迭代器的 const 版本中继承了 zip 迭代器接口,所以它并不像例子所示那样简单。 - Steve

3
根据const的定义,函数不应修改对象的状态。但是如果调用另一个非const成员,则可能会更改对象的状态,因此不允许这样做。
我知道你说你不想听这个,但我认为对于其他人来说这很重要。

3

const成员方法的限制来自编译时。如果您能愚弄编译器,那么是的。

class CFoo
{ 
public:
    CFoo() {m_Foo = this;}
    void tee();

    void bar() const 
    { 
        m_Foo->m_val++;  // fine 
        m_Foo->tee();    // fine
    }
private:
   CFoo * m_Foo;
   int    m_Val;  

};

实际上,这样做废除了const成员函数的目的,因此在设计新类时最好不要这样做。但是知道有一种方法可以做到这一点也没有害处,特别是在那些不太适合概念的旧类中,可以用作解决方法。


2

const的重载:

const int& Second() const
{
    return m_bar;
}

你可以添加这个方法并保留原始的非const版本。

1

迭代器在这方面非常相似,是一个有趣的研究对象。

const迭代器通常是“非const”迭代器的基础,你经常会发现const_cast<>()或C风格的转换被用来从子类中的访问器丢弃基类中的const。

编辑: 评论如下:

我有一个zip迭代器,其中const继承自非const

如果您说的是我所想的,这通常是错误的继承结构,原因是子类不应该比父类限制更少。

假设您有一些算法使用您的zip迭代器,将const迭代器传递给非const是否合适?

如果您有一个const容器,只能要求它提供一个const迭代器,但然后const迭代器是从迭代器派生的,因此您只需使用父级上的功能即可获得非const访问。

以下是建议的继承快速概述,遵循传统的STL模型。

class ConstIterator: 
    public std::_Bidit< myType, int, const myType *, const mType & >
{
  reference operator*() const { return m_p; }
}

class Iterator : public ConstIterator 
{
  typedef ConstIterator _Mybase;
  // overide the types provided by ConstIterator
  typedef myType * pointer;
  typedef myType & reference;

  reference operator*() const
  { 
    return ((reference)**(_Mybase *)this);
  }
}

typedef std::reverse_iterator<ConstIterator> ConstReverseIterator;
typedef std::reverse_iterator<Iterator> ReverseIterator;

我倾向于将迭代器编写为模板类,并使用类型特征来操作成员函数的const属性;这避免了需要使用const_cast,使代码更易于阅读。 - James McNellis
听起来很有趣,但它是否允许在可能使用const_iterator的地方使用迭代器?我对我看到的STL(主要是MSVC)做出了这个原始观察。 - Greg Domjan
这实际上是我的用例。我有一个zip迭代器,其中const迭代器从non-const继承而来。@James,你能用类型特征回答这个问题吗?如果可能的话,我真的很想避免这种情况。 - Steve
@Steve:我会试着找些资料;目前我没有存放所有项目的个人电脑。通常的方法是创建一个单一类模板template <typename T> class iterator_impl,然后在使用它的容器中提供一个 typedef iterator_impl<const T> const_iteratortypedef iterator_impl<T> iterator。然后你可以从 T 中提取 constness,适当地设置所有返回类型和指针等等。这是你要的吗?(我只是想确保我完全了解你需要什么)。 - James McNellis

0

我发现自己试图调用一个非const成员函数,但由于我使用的API,它实际上是const的。最后,我选择了另一种解决方案:重新协商API,以便我继承的函数正确地是const。

并不总是可能协商更改他人的函数,但在可能的情况下这样做似乎比需要使用const_cast更干净、更好,并且也有益于其他用户。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接