【问题标题】:Call a non-const member function from a const member function从 const 成员函数调用非常量成员函数
【发布时间】:2010-10-28 21:06:12
【问题描述】:

我想知道是否可以从 const 成员函数调用非常量成员函数。在下面的示例中,First 给出了编译器错误。我明白为什么它会出错,我想知道是否有办法解决它。

class Foo
{
   const int& First() const
   {
         return Second();
   }

   int& Second()
   {
        return m_bar;
   }

   int m_bar;
}

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

【问题讨论】:

标签: c++ constants


【解决方案1】:
return (const_cast<Foo*>(this))->Second();

然后安静地哭。

【讨论】:

  • 如果函数名称相同怎么办? const int& GetBar() const; int&GetBar();
  • 你可以在 constness 上重载一个函数,这样它就可以工作了。
【解决方案2】:

可能

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

int& Second() { return m_bar; }

我不会推荐这个;它既丑陋又危险(const_cast 的任何使用都是危险的)。

最好将尽可能多的常用功能转移到辅助函数中,然后让 const 和非 const 成员函数各自做尽可能少的工作。

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

【讨论】:

  • 或者把int&amp; Second()变成int&amp; Second() const。非const 成员函数调用Second() 没有问题,但现在const 成员函数也可以调用它而无需任何诡计。
  • @Jeremy:那么你认为我们如何返回对成员的非常量引用?
  • @GMan:Touché。你不能不将m_bar 声明为mutable int。 @Fred Larson 的建议非常好,但它所引导的方向 - 将每个受祝福的访问器编写两次,一次 const-modified 和一次不 - 并不那么令人愉快。
  • @Jeremy:在大多数情况下(不是全部,但大多数情况下),没有必要拥有非常量访问器(至少这是我的经验)。即使您确实需要这两个函数,因为无论如何您都必须编写这两个函数,所以在这两个函数中编写 return m_bar; 通常不会造成太大的额外负担。
  • 问题是在 zip 迭代器的 const 版本中继承了 zip 迭代器接口,因此它不像示例所暗示的那么简单
【解决方案3】:

根据const 的定义,函数不应修改对象的状态。但是如果它调用另一个非常量成员,对象的状态可能会改变,所以它是不允许的。

我知道你说过你不想听到这个,但我认为这对其他人来说很重要。

【讨论】:

    【解决方案4】:

    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成员函数的目的,所以在设计新类时最好不要这样做。知道有办法做到这一点并没有什么坏处,特别是它可以用作这些在 const 成员函数概念上设计得不好的旧类的变通方法。

    【讨论】:

      【解决方案5】:

      const 上的重载:

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

      您可以添加此方法并保留原始的非常量版本。

      【讨论】:

        【解决方案6】:

        迭代器在这方面是相似的,并进行了有趣的研究。

        const 迭代器通常是“非 const”迭代器的基础,您经常会发现 const_cast&lt;&gt;() 或 C 风格的转换用于从具有子类访问器的基类中丢弃 const。

        编辑: 评论是

        我有一个 zip 迭代器,其中 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_cast 的需要,并使代码更易于阅读。
        • 听起来很有趣,但它是否允许在可能使用 const_iterator 的地方使用迭代器?我正在对我所见过的 stl 进行这个原始观察 - 这主要是针对那些使用 MSVC 的。
        • 这实际上是我的用例。我有一个 zip 迭代器,其中 const 继承自非常量。 @James,你能用类型特征的答案来回答这个问题吗?如果可能的话,我真的很想不这样做
        • @Steve:我会尝试挖掘一些东西;目前我的电脑上没有我的所有项目。一般的方法是创建单个类模板template &lt;typename T&gt; class iterator_impl,然后在使用它的容器中提供typedef iterator_impl&lt;const T&gt; const_iteratortypedef iterator_impl&lt;T&gt; iterator。然后,您可以在iterator_impl 模板中从T 中提取常量,并适当地设置所有返回类型和指针等。那是你要找的吗? (我只是想确保我完全理解您的需求)。
        【解决方案7】:

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

        并不总是可以协商更改其他人的功能,但在可能的情况下这样做似乎比需要使用 const_cast 更清洁和更好,并且对其他用户也有好处。

        【讨论】:

          猜你喜欢
          • 2016-08-17
          • 1970-01-01
          • 2019-07-09
          • 2012-12-13
          • 1970-01-01
          • 2021-08-10
          • 2019-05-04
          相关资源
          最近更新 更多