【问题标题】:Why does const_cast need to state what you're casting to?为什么 const_cast 需要说明你要转换的内容?
【发布时间】:2011-05-21 23:46:54
【问题描述】:

根据标准(第 5.2.11 节),const_cast 会丢弃 cv 限定符(const 或 volatile)。

这是一个简单的例子。首先你声明两个函数接受一个指针和一个引用:

class Bar { ... };
void foo_ptr(Bar*);
void foo_ref(Bar&);

然后你创建一个对 const 的引用:

Bar b;
const Bar& cb = b;

然后您可以使用适当的 const_cast 调用任一函数:

foo_ptr(const_cast<Bar*>(&cb));
foo_ref(const_cast<Bar&>(cb));

这是我的问题:既然 const_cast 无法完成其他演员的设计目的,那么您要转换的内容不是很明显吗?换句话说,为什么语言不允许我简单地说:

foo_ptr(const_cast<>(&cb));
foo_ref(const_cast<>(cb));

我只能想到以下两个原因:

a) 当我尝试做一些疯狂的事情时,编译器应该阻止我,例如:

foo_ptr(const_cast<int*>(&cb));
foo_ref(const_cast<int&>(cb));

通过强迫我明确说明我要转换的类型可以防止我行为不端。我觉得这个(假设的)解释很弱,因为如果语言倾向于允许我写下错误的东西只是让编译器纠正我,那将是很奇怪的。

b) 如果变量既是 const 又是 volatile,则可能存在歧义。在这种情况下,编译器将无法判断我是否试图抛弃其中一个或另一个(或两者)。

这是为什么,还是有其他原因?

【问题讨论】:

    标签: c++ casting standards


    【解决方案1】:

    主要原因是使用const_cast你可以从

    Foo * const * volatile * * 
    

    Foo * * * volatile * const
    

    目标类型是什么真的很明显吗?

    【讨论】:

      【解决方案2】:

      我认为 James McNellis 已经谈到了重要的原因。 const_cast可以添加和删除常量/易失性。你想要什么并不总是很清楚。

      如果我在const foo 类型的参数上调用const_cast,这是否意味着我想将其设为非const,或者添加volatile?还是应该保留 constqualifier?如果我在类型为(非 const)foo 的对象上调用它,是否应该添加 const?还是应该假设我想删除 const-ness,并返回一个类型为(非 const)foo 的对象?

      另一个原因可能只是一致性。

      我们有static_cast&lt;T&gt;(x)dynamic_cast&lt;T&gt;(x)reinterpret_cast&lt;T&gt;(x),所以如果没有类似模板的语法,const_cast(x) 看起来会很奇怪。

      【讨论】:

      • 关于 adding const:好的,但这只是取代了问题(因为您可以通过其他方式添加 const)使其成为“为什么 const_cast 需要允许您添加 const ?”。
      • 关于一致性:是的,确实,这是一个合理的观点。
      【解决方案3】:

      const_cast 可用于添加或删除 constvolatile 限定符。因此,如果允许这样的语法,则以下所有内容都是const_cast&lt;&gt;(&amp;cb) 的合法目标类型:

      Bar*                 (1)
      const Bar*           (2)
      volatile Bar*        (3)
      const volatile Bar*  (4)
      

      您打算(1)(2) 通常是愚蠢的,但可以想象它可能出现在某个地方,也许在某些模板代码中。 (3)(4) 确实是问题所在:您可以删除 const 资格并添加 volatile 资格,只需一次演员。

      您可以将现有的const_cast 替换为一对演员表const_castvolatile_cast,并禁止大小写(2);那么您可以在没有目标类型的情况下使用它们中的任何一个。但是,要知道强制转换表达式的类型就比较困难了。要知道强制转换表达式是添加还是删除限定条件,您必须知道源表达式的类型是什么。

      你没有理由不能使用函数模板来获得你想要的东西:

      template <typename T>
      T* remove_const(const T* p) {
          return const_cast<T*>(p);
      }
      

      您可以轻松地为 remove_volatileadd_constadd_volatile 编写类似的函数,它们都是隐式的。

      【讨论】:

      • 当然操作符也允许const_cast&lt;&gt;(&amp;b)(其中b不是const)和相同的结果类型集,所以即使它没有处理波动,你也得问亚历山德罗斯const_cast 是否应该切换 constness 或默认添加或删除它(后者会使它有点用词不当)。正如您所说,在模板中,通常希望在不知道您从什么开始的情况下获得 const 或非 const 类型。
      • @Tony:关于 b 不需要是 const 的好处。更一般地说,即使我知道 const_cast 也可以添加 const,但我的隐含假设是没有人这样做(因为还有其他方法可以做到),因此如果输入 b 不是 const,你就让它成为。
      • @Tony: static_cast 在这里更可取,因为虽然它可以添加常量,但它明确不能删除常量。或者,通常按照template &lt;typename T, typename U&gt; T implicit_cast(U x) { return x; } 的行编写implicit_cast 函数模板。
      • @Tony:虽然这不构成证据,但詹姆斯在这方面做得很好。在 Scott Meyers 的 Effective C++ 第 3 版的第 3 项中。他正是这样做的(使用 static_cast 添加 const)并声明:“添加 const 的强制转换只是强制进行安全转换(从非 const 对象到 const 对象),所以我们为此使用 static_cast。那个移除 const 只能通过 const_cast 来完成,所以我们真的别无选择。”
      • @Tony:const_cast 具有相同的风险:您可能无意中删除了 volatile 资格,而您只打算删除 const 资格。 const_cast 只能用于删除资格;这样,它更容易在源代码中脱颖而出。 implicit_cast的好处是100%安全;除了在没有任何强制转换的情况下允许的转换之外,您不能使用它执行任何转换。
      猜你喜欢
      • 1970-01-01
      • 2010-12-10
      • 1970-01-01
      • 1970-01-01
      • 2014-11-13
      • 2014-10-08
      • 1970-01-01
      • 2014-03-13
      相关资源
      最近更新 更多