【问题标题】:Good practice: Constant to non-constant cast良好实践:常量到非常量转换
【发布时间】:2014-06-23 16:56:14
【问题描述】:

当一个函数不修改对象参数时,我总是让它请求一个常量引用,即使引用的对象不是真正的常量。这是错的吗?

对于包装类,我想这样写:

template<class B>
class Wrapper{
private:
  B* base_;
public:
  Wrapper(const B& b) { base_ = const_cast<B*>(&b); }
  void ModifyBase();
};

构造函数不会修改基类,因此它要求一个常量引用。

包装器有一些方法需要修改基数,因此它需要存储一个非常量指针(因此是转换)。

我觉得我的解决方案不是最好的。

有没有更好的方法来做到这一点?

有什么公认的约定吗?

【问题讨论】:

  • 我想如果你想修改参数是错误的。据我了解,const 在参数中的意思是“我只需要看到这个值 - 我不会用它做任何事情!”。
  • X const x; Wrapper&lt;X&gt; w(x); w.ModifyBase(); 你有未定义的行为。不要剥离 const,除非您希望 only 弄乱类型系统(不推荐使用的 API,DRY)。如果您打算修改对象,请不要去掉 const。

标签: c++ casting constants conventions


【解决方案1】:

当您选择参数作为 const 引用时,您是在告诉用户“您可以相信,如果您向我传递一个对象,它就不会被修改 [通过此引用]†。”您应该尽可能多地这样做,因为用户可以通过查看类型来更多地了解您的函数将做什么和不做什么。此外,传递可变引用可能会导致代码难以推理。

但是,在您的问题中,您的const 并没有说实话。它抛弃了constness 并存储了一个非const 指针——这意味着该对象很可能会被修改。你骗了用户!构造函数本身对对象不做任何事情并不重要。它允许它被其他成员函数修改。这是不好的行为。您的构造函数不应使用 const 引用。

不仅如此,您当前的实现还允许未定义的行为。即使将最初声明为const 的对象提供给您的Wrapper,它也不在乎。它抛弃了 constness 并允许其他成员函数对其进行修改。修改最初为 const 的对象是未定义的行为。

† 见 6502 的评论

【讨论】:

  • 这并不完全正确。承诺是“如果你传给我一个对象,我不会使用这个引用修改它”;可以打电话给例如一个知道该对象并对其进行变异的全局函数。作为另一个示例,如果您将同一对象两次传递给接受 const 引用和非 const 引用的函数,则该对象可以被改变(使用第二个引用):传递 const 引用并不意味着不修改对象的承诺. “const 引用”承诺仅与该引用有关,不涉及对象。
  • @6502 这是不正确的:通过 const 和非 const 引用传递相同的对象,通过 const 引用的 const_cast 应用的修改是未定义的行为!
  • @DieterLücking:您似乎错过了“(使用第二个参考)”部分。请注意,无论如何你都错了:如果对象不是常量(这就是 const_cast 的设计目的),则从 const 引用中丢弃 const 并使用它来修改对象是非常好的。
  • @6502 看看:7.1.6.1 (3/4)
  • @DieterLücking:那是在谈论const 对象。 const 对象和 const 引用是 C++ 中两个非常不同的概念。
【解决方案2】:

ctor 不会改变ctor 中的对象并不重要,ctor 完成后发生的事情就是为什么你需要一个非const 对象指针指向@987654325 @。所以它与传入的B 对象的所有权和生命周期有关:如果你想获得所有权(通过&amp; 引用,那么该对象必须是非const,因为它可以被更改。如果你想简单地复制传入的B 对象,然后不要使用引用,按值传递并存储指向副本的指针。

【讨论】:

  • 我们通常将“所有者”称为负责销毁对象的代码部分。所以&amp;是所有权的转移。
猜你喜欢
  • 1970-01-01
  • 2012-09-20
  • 1970-01-01
  • 2011-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
相关资源
最近更新 更多