【问题标题】:Calling a const function from a non-const object从非常量对象调用 const 函数
【发布时间】:2010-09-29 06:10:18
【问题描述】:

我需要从非常量对象调用 const 函数。看例子

struct IProcess {
   virtual bool doSomeWork() const = 0L;
};
class Foo : public IProcess {    
  virtual bool doSomeWork() const {
    ...
  }
};

class Bar
{
public:
   const IProcess& getProcess() const {return ...;}
   IProcess& getProcess() {return ...;}

   void doOtherWork {
    getProcess().doSomeWork();        
  }
};

打电话

getProcess().doSomeWork();

总会导致调用

IProcess& getProcess()

还有其他方法可以调用

const IProcess& getProcess() const 

来自一个非常量的成员函数? 到目前为止我用过

const_cast<const Bar*>(this)->getProcess().doSomeWork();

这可以解决问题,但似乎过于复杂。


编辑:我应该提到代码正在重构,最终只剩下一个函数。

const IProcess& getProcess() const 

但是,目前存在副作用,有时 const 调用可能会返回不同的 IProcess 实例。

请继续话题。

【问题讨论】:

    标签: c++ function constants


    【解决方案1】:

    const_cast 用于铸造 远离 constness!

    您正在从非常量转换为安全的 const,因此请使用static_cast

       static_cast<const Bar*>(this)->getProcess().doSomeWork();
    

    我的意思是从技术上讲,您可以使用const_cast 进行常量转换,但这不是对运算符的务实使用。新风格演员表(相对于旧的 c 风格演员表)的目的是传达演员表的意图。 const_cast 是一种代码味道,至少应该对其使用进行审查。另一方面,static_cast 是安全的。但这是 C++ 风格的问题。

    或者您可以创建一个新的(私有)const 方法,并从doOtherWork 调用它:

      void doSomeWorkOnProcess() const { getProcess().doSomeWork(); }
    

    使用 const 临时变量也是一种选择(由“MSN”回答):

       const Bar* _this = this;
       _this->getProcess().doSomeWork();
    

    【讨论】:

    • 您实际上可以安全地做到这两点 - 抛弃并投入 constness。
    • 是的,但 mfazekas 的观点是,你只需要 const_cast 来移除 constness,使用“能完成这项工作的最小武器”符合你自己的利益"(即 static_cast),因为这样所有出现的 const_cast 都是代码味道,很容易被 grep 发现。
    【解决方案2】:

    避免强制转换:将其分配给 const Bar * 或其他任何东西,然后使用它来调用 getProcess()

    这样做有一些迂腐的理由,但它也让你在不强制编译器做一些潜在不安全的事情的情况下更清楚你在做什么。诚然,您可能永远不会遇到这些情况,但您不妨编写一些在这种情况下不使用强制转换的东西。

    【讨论】:

    • 你所说的迂腐的原因是什么?我会认为一个丑陋的演员会让你的意图比分配给 const Bar * 更清楚,但这只是我的看法。
    • 这往往会导致意外行为的唯一方法是当 getProcess() 的返回值更改其 cv 限定符(即变得易变)时。 const_cast 会成功,但与最初的意图相比,它会做一些非常不同的事情。一项任务会抓住这一点。微信
    【解决方案3】:

    如果演员表对您来说太难看,您可以改为向 Bar 添加一个方法,该方法仅返回对 *this 的 const 引用:

    Bar const& as_const() const {
        return *this;    // Compiler adds "const" without needing static_cast<>
    }
    

    然后您可以在Bar 中调用任何 const 方法,只需在前面加上as_const().,例如:

    as_const().getProcess().doSomeWork();
    

    【讨论】:

      【解决方案4】:

      如果getProcess()getProcess() const 没有返回对同一对象的引用(但限定不同),则表明class Bar 的设计不佳。重载函数的constness 并不是区分具有不同行为的函数的好方法。

      如果它们返回对同一对象的引用,则:

      const_cast<const Bar*>(this)->getProcess().doSomeWork();
      

      getProcess().doSomeWork();
      

      调用完全相同的doSomeWork() 函数,因此无需使用const_cast

      【讨论】:

      • “Copy on write”是 const/non const 可以返回不同对象的一个​​例子,所以它并不总是糟糕的设计。
      • 我仍然会说,在 const 上重载并返回除代理类之外的任何内容都是实现写入时复制的糟糕方法。如果碰巧有非常量引用的东西必须执行 const_cast 以避免悲观复制,这通常会被遗漏并且性能会受到影响。
      【解决方案5】:

      如果函数没有重载,你不必做任何转换技巧。调用非常量对象的 const 方法很好。它从被禁止的 const 对象调用非常量方法。如果方法被 const 和非 const 函数覆盖,那么将对象转换为 const 就可以了:

      const_cast<const IProcess&> (getProcess()).doSomeWork();
      

      编辑:我没有阅读整个问题。是的,您需要对 this 指针进行 const_cast 或将 doOtherWork 函数设为 const 以调用 const IProcess& getProcess() const

      重点仍然是您不需要 const 对象来调用 doSomeWork。既然是目标,那你需要调用 const 方法吗?

      另一个选择是重命名被覆盖的函数。如果这两个函数实际上具有不同的行为/副作用,这将是一个非常好的主意。否则,函数调用的效果不会很明显。

      【讨论】:

      • 我在发布之前尝试了上述方法。令我惊讶的是,非常量函数无论如何都被调用了。我将在星期一再次确认这一点。我正在使用 MS VS6 - 可能是编译器问题。感谢您的快速回答。
      • getProcess 函数有副作用吗?否则,调用哪一个又有什么关系呢?
      • 实际上,正如 mfazekas 指出的那样,在这种情况下使用 static_cast 而不是 const_cast 就足够了,因为您正在添加而不是删除 constness,这始终是一种安全的操作。
      【解决方案6】:

      定义一个模板

      template< class T >
      const T & addConst ( T & t ) 
      {
          return t;
      }
      

      然后打电话

      addConst( getProcess() ).doSomeWork();
      

      【讨论】:

        【解决方案7】:

        嗯,你能声明一下

        void doOtherWork const ()
        

        ?

        这样就可以了。

        【讨论】:

        • 不幸的是,没有。 doOtherWork() 必须是非常数。
        • 那么可能是这里错误地建议了覆盖。 getProcess() const 和 plain 之间有什么区别?是缓存吗?如果是这样,官方的解决方案是“可变的”。
        【解决方案8】:

        我认为 const_cast 方法是您的最佳选择。这只是 C++ 中 const 框架的一个限制。我认为您可以避免强制转换的唯一方法是定义一个无论如何都返回 const IProcess 实例的方法。例如。

        const IProcess* getProcessConst() const { return ... }
        ...
        getProcessConst().doSomeWork();
        

        【讨论】:

          【解决方案9】:

          我假设您希望 DoOtherWork 调用您的两个 getprocess 调用之一,具体取决于它是否从 const 对象调用。

          我能建议的最好的是:

          class Bar
          {
          public:
             const IProcess& getProcess() const {return ...;}
             IProcess& getProcess() {return ...;}
          
             void doOtherWork {            // should use getProcess()      
              getProcess().doSomeWork();        
            }
             void doOtherWork const {
              getProcess().doSomeWork();   // should use getProcess() const     
            }
          };
          

          即使这样行​​得通,这对我来说也是一种难闻的气味。我会非常警惕类行为会根据对象的常量而发生根本性的变化。

          【讨论】:

          • 每个只有一个定义 - void doOtherWork() - virtual bool doSomeWork() const
          【解决方案10】:

          由 monjardin 发布
          另一种选择是重命名被覆盖的函数。如果这两个函数实际上具有不同的行为/副作用,这将是一个非常好的主意。否则,函数调用的效果不会很明显。

          IProcess& 在其他代码中主要通过属性访问

          __declspec(property(get=getProcess)) IProcess& Process;
          

          所以重命名不是一种选择。大多数时候调用函数的 constness 匹配 getProcess() 所以没有问题。

          【讨论】:

            【解决方案11】:

            您基本上被重命名其他方法或 const_cast 所困扰。

            顺便说一句,这是写时复制智能指针在 C++ 中实际上不能正常工作的原因之一。写时复制智能指针是可以无限共享的。当用户在非常量上下文中访问数据时,会制作数据的副本(如果用户不持有唯一引用)。当共享只有部分客户端需要修改的大型数据结构时,这种指针可以非常方便地使用。最“合乎逻辑”的实现是有一个 const 和一个非常量 operator->。 const 版本只返回底层引用。非常量版本进行唯一的引用检查和复制。它没有用,因为非常量智能指针默认使用非常量运算符->,即使您想使用 const 版本。 const_cast 要求使其对用户非常不友好。

            我欢迎任何证明我错了并在 C++ 中显示用户友好的写时复制指针的人...

            【讨论】:

            • 您的解决方案无论如何都无法正常工作; smart_cow_ptr 无法知道 T 是否有可变成员。
            • 这个例子的重点是它不能以一种有用的方式工作。如果您使用 const_cast&>(ptr)->some_const_member(),它会正常工作,但是需要执行 const_cast 恕我直言,使用牛指针太痛苦了。
            猜你喜欢
            • 2019-11-20
            • 1970-01-01
            • 2018-08-14
            • 1970-01-01
            • 2019-07-09
            • 1970-01-01
            • 1970-01-01
            • 2019-04-21
            相关资源
            最近更新 更多