const_cast<>()
用于C ++代码通常被认为是一种不好的实践,因为它揭示了(大多数情况下)设计上的缺陷。虽然我完全同意这一点,但我想知道在哪些情况下使用
const_cast<>()
是可以接受和唯一的解决方案。你们能否给我一些你们所知道或遇到的例子?
非常感谢。
const_cast<>()
用于C ++代码通常被认为是一种不好的实践,因为它揭示了(大多数情况下)设计上的缺陷。const_cast<>()
是可以接受和唯一的解决方案。这个功能主要是为那些遗留API而设计的,这些API不符合const正确性,也就是说,你不能更改函数,它们具有非const接口,但实际上并不在接口上进行任何修改。
readv
/writev
,其中 struct iov
具有 void* data
而不是 void const* data
,因为 readv
写入它,但 writev
不会。 - user4945014正如其他人所说,它的主要目的是从对象中去除 const,以便传递给你知道不会修改参数的非 const correct 函数。
有一个技巧(由 Meyers?)可以避免代码重复,方法如下:
struct foo
{
const return_type& get(void) const
{
// fancy pants code that you definitely
// don't want to repeat
return theValue; // and got it
}
return_type& get(void)
{
// well-defined: Add const to *this,
// call the const version, then
// const-cast to remove const (because
// *this is non-const, this is ok)
return const_cast<return_type&>(static_cast<const foo&>(*this).get());
}
};
get()
中复制内容更糟糕。在这种情况下,模板助手(为const和非const各实例化一次)甚至更好,因为它可以让编译器验证花哨的代码是否在this
可修改的情况下确实返回了可修改的东西。但有时它可能会返回一个指向const全局变量的引用,所以这个技巧强制你手动验证花哨的代码的const正确性。不好。 - Steve Jessopget
只返回一个数据成员,那该怎么办?你会避免复制粘贴吗?我的观点是,这种技巧没有好的情况。要么代码足够简单,可以复制粘贴,要么它太复杂了,无法手动分析const-correctness。没有中间地带,既不需要自动化的const-correctness,又不能太多复制。在Meyer的例子中,我记得花哨的代码是边界检查:所以把检查放在一个辅助函数中,就像我们通常共享代码一样;-) - Steve Jessoptemplate <typename SELF, typename RET> RET &get_helper(SELF &self) { /* 炫酷的代码 */ return self.theValue; }
。然后在常量 get
中调用它,如 get_helper<const foo, const return_type>(*this)
,在非常量中调用 get_helper<foo, return_type>(*this)
。 - Steve Jessopthis
的使用向您道歉,您毫无疑问也已经熟悉它了;-)。那只是一种简单的方法,在GMan的例子中,您可以轻松使用C++0x的decltype
之一来消除一个模板参数。 - Steve Jessop我同意你的说法,它的正常用途是因为需要隐藏一个“设计缺陷”。
在我的经验中,典型的使用场景之一是当您尝试将C++与现有的C代码进行接口时。许多现有的C代码将C字符串作为char *
传递,即使字符串不被修改,它们通常表示为在C ++中转换为const char *
。这是两种语言之间的阻抗不匹配,您通常会通过使用const_cast来解决它。当然,你最好非常确定你正在接口的代码不会对传入的数据做出任何聪明的修改。
我认为在新编写的代码中使用const_cast
是代码异味,但是对于与旧的C和C++代码进行接口而言,这是一个必要的恶。话虽如此,我会对需要const_cast
进行警惕,因为对于任何非POD对象来说,这通常是应该在设计层面而不是代码层面解决的问题。
在我看来,一个合法的使用场景是与 std::set
迭代器一起使用。它们始终是 const
,以防止更改集合中使用的键。更改键将破坏集合的内部结构并导致未定义的行为。
但是,只要键不改变,就可以安全地更改对象中的其他数据。
假设您有一个像这样的 std::set
:
std::set<MyObject> some_set;
同时,以下是一个类似于这样的类:
class MyObject {
public:
MyObject(const std::string &key)
: key_(key) {}
bool operator<(const MyObject &other) const {
return key_ < other.key_;
}
private:
// ...
// <some other data>
// ...
const std::string key_;
};
const
引用:const MyObject &object = *some_set_iterator;
但是由于关键字是const
,因此安全地将解引用的迭代器进行const_cast
操作:
MyObject &object = const_cast<MyObject &>(*some_set_iterator);
这种使用非常合理,当您有一个 const 和非 const api(分别针对 const 和非 const 对象)时,就可以使用它。
class Bar {
const SomeType& foo() const;
SomeType& foo();
}
既然我们不想在两个函数中重复代码,因此通常会使用
class Bar {
SomeType& foo() {
//Actual implementation
}
const SomeType& foo() const {
return const_cast<Bar*>(this)->foo();
}
};
当然,这是建立在foo没有违反const语义的前提下。
foo
函数的非const版本可能会修改this
指向的对象,因此在将一个const对象转换为非const后调用它是“不安全”的。在非const对象上调用const成员函数是“安全”的,因此建议使用GMan的实现方式。但是,当客户端修改返回值时,GMan的实现方式也可能导致未定义的行为,正如我在他的答案中所评论的那样。 - Steve Jessop在C++ Primer第五版书中有一个const_cast使用的示例。下面的函数返回一个指向常量字符串的引用
// return a reference to the shorter of two strings
const string &shorterString(const string &s1, const string
&s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
该书随后提到了当我们需要一个非const引用时的情况。
我们可以在一对非const字符串参数上调用函数,但是我们将得到一个指向const字符串的引用作为结果。我们可能希望有一个shorterString的版本,当给定非const参数时,它会产生一个普通引用。我们可以使用const_cast编写此函数的版本:
string &shorterString(string &s1, string &s2)
{
auto &r = shorterString(const_cast<const string&>(s1),
const_cast<const string&>(s2));
return const_cast<string&>(r);
}
根据书上所说,如果我们知道转换是安全的,则应使用它。
当你调用的代码无法修改且不是const正确时,当然可以使用。需要注意的是,你只应该在调用你确定不会修改数据的函数时使用它!
const_cast
的方法,这可能是观众所关心的。 - Alexandre C.