从非const对象调用const函数

24

我需要从一个非const对象中调用一个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实例。

请保持主题。

12个回答

16

const_cast 用于去掉常量性的转换!

你要从非常量类型到常量类型进行转换,这是安全的,所以应该使用 static_cast:

   static_cast<const Bar*>(this)->getProcess().doSomeWork();
< p > 我的意思是,从技术层面上讲,您可以使用const_cast进行常量转换,但这不是运算符的实用用途。新样式强制转换(与旧的C样式强制转换相比)的目的是传达强制转换的意图。const_cast是代码异味,其使用应至少进行审核。另一方面,static_cast是安全的。但这是C ++风格的问题。

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

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

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

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

2
你其实可以安全地同时进行强制转换和取消常量性。 - Chris
5
是的,但mfazekas的观点是,你只需要const_cast<>来去除const属性,而且使用“最小的武器完成工作”(即static_cast<>)符合你自己的利益,因为这样所有const_cast的出现都是可以轻松被搜索到的代码异味。 - j_random_hacker
static_cast<> 可能会无意中更改实际类型,如果目标类型与原始类型存在结构关系(即一个是从另一个派生而来)。const_cast<> 只能添加或删除 const 属性。因此,const_cast<const*T> 比 static_cast<const*T> 更安全。这是令人遗憾的,因为将 const_cast 视为代码异味肯定是很好的,但是 const_cast<const*T> 不能被视为代码异味(至少不比 static_cast<> 更多)。 - John Doggett
实际上,使用C++ 17,您可以通过使用std::as_const(*this)来保持const_cast<>是一种不好的做法。在任何情况下最好避免使用static_cast<>。 - John Doggett

11
避免使用强制类型转换:将其分配给一个const Bar *或类似的变量,然后使用该变量调用getProcess()
这样做有一些学究式的原因,但它还可以更清楚地表明你的操作,并避免强制编译器执行可能不安全的操作。当然,你可能永远不会遇到这种情况,但在这种情况下最好编写不使用强制类型转换的代码。

3
你所提到的学究式的理由是什么?我原本认为一个又大又丑的const Bar * 变量会比一个赋值给它的变量更明确表达你的意图,但这只是我的看法。 - j_random_hacker
1
当getProcess()的返回值改变其cv-限定符(即变为volatile)时,这往往会导致意外行为。const_cast<>将成功,但与原始意图相比,它将执行完全不同的操作。赋值可以捕捉到这种情况。 - MSN

5
如果类型转换对您来说太丑陋了,您可以改为向Bar添加一个方法,该方法仅返回*this的常量引用:
Bar const& as_const() const {
    return *this;    // Compiler adds "const" without needing static_cast<>
}

您可以通过在as_const()前缀中加入Bar来调用任何const方法,例如:

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

4
如果 getProcess()getProcess() const 返回的不是同一个对象的引用(但限定方式不同),那么这就说明 class Bar 的设计不够好。通过函数的const性来重载,不是区分具有不同行为的函数的好方法。
如果它们返回对同一对象的引用,则:
const_cast<const Bar*>(this)->getProcess().doSomeWork();

并且

getProcess().doSomeWork();

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


1
“写时复制”是一个例子,当const/non const返回不同的对象时,它并不总是坏设计。 - mfazekas
我仍然认为,在常量重载和返回任何非代理类的情况下实现写时复制是一种不好的方式。如果有些东西只具有非常量引用并且必须执行const_cast以避免悲观复制,则经常会被忽略,性能也会受到影响。 - CB Bailey

2

定义一个模板

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

并调用

addConst( getProcess() ).doSomeWork();

1
如果函数没有重载,你就不需要进行任何强制转换技巧。调用一个非const对象的const方法是可以的,但是从一个const对象调用非const方法是被禁止的。如果这些方法被一个const和非const函数覆盖,那么将对象转换为const类型就可以解决问题:
const_cast<const IProcess&> (getProcess()).doSomeWork();

编辑:我没有读完整个问题。是的,你需要使用const_cast来转换this指针或将doOtherWork函数设为const才能调用const IProcess& getProcess() const

重点在于,你不需要一个const对象来调用doSomeWork。既然这是目标,那么你需要调用const方法吗?

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


在发布之前,我已经尝试过上述方法。令我惊讶的是,非const函数仍然被调用了。我将于周一再次确认。我正在使用MS VS6——可能是编译器问题。感谢快速回答。 - Chris
getProcess函数有副作用吗?否则,为什么调用哪个函数会有影响呢? - Judge Maygarden
实际上,正如mfazekas所指出的那样,在这种情况下使用static_cast<>就足够了,因为你是添加而不是移除const属性,这总是一项安全操作。 - j_random_hacker

0

对于 C++17 及更高版本,请考虑使用 std::as_const

std::as_const(getProcess()).doSomeWork()

0

好的,你能声明吗?

void doOtherWork const ()

?

那就可以了。

很遗憾,不行。doOtherWork()必须是非常量的。 - Chris
1
也许在这里重写是错误的建议。getProcess() const和plain之间有什么区别?是否缓存了?如果是,官方解决方案是“mutable”。 - user3458

0

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

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

0

我假设您希望DoOtherWork根据调用方是否为const对象之一来调用getprocess调用。

我能提供的最佳建议是:

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 - Chris

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