判断库中的函数是否实际修改任何内容有多容易?
如果很容易分辨,而他们没有,那么您可以const_cast
将 const 指针/引用指向非 const 并调用库函数。您可能希望在库类周围放置一个包装器来为您执行此操作,这既乏味又冗长,但会从您的类中获取该代码。这个包装器可能是一个添加一些 const 访问器的子类,这取决于您使用库类的方式是否允许它工作。
If it's hard to tell, or they do modify things, then you need to use non-const instances and references to the library classes in your code. mutable
can help with those of type (2), but for those of type (1) you just need to pass non-const arguments around.
For an example of why it might be hard, consider that the library author might have written something like this:
struct Foo {
size_t times_accessed;
int value;
int get() {
++times_accessed;
return value;
}
};
Now, if you const_cast
a const
instance of Foo
and call get()
, you have undefined behavior[*]. So you have to be sure that get
really doesn't modify the object it's called on. You could mitigate this a bit, by making sure that you never create any const instances of Foo
, even though you do take const references to non-const instances. That way, when you const_cast
and call get
you at least don't cause UB. It might make your code confusing, that fields keep changing on objects that your functions claim not to modify.
[*] Why is it undefined behavior? It has to be, in order that the language can guarantee that the value of a const
object never changes in a valid program. This guarantee allows the compiler to do useful things. For example it can put static const
objects in read-only data sections, and it can optimize code using known values. It also means that a const
integer object with a visible initializer is a compile-time constant, which the standard makes use of to let you use it as the size of an array, or a template argument. If it wasn't UB to modify a const object, then const objects wouldn't be constant, and these things wouldn't be possible:
#include <iostream>
struct Foo {
int a;
Foo(int a) : a(a) {}
};
void nobody_knows_what_this_does1(const int *p); // defined in another TU
void nobody_knows_what_this_does2(const int *p); // defined in another TU
int main() {
const Foo f(1);
Foo g(1);
nobody_knows_what_this_does1(&f.a);
nobody_knows_what_this_does2(&g.a);
int x;
if (std::cin >> x) {
std::cout << (x / f.a); // Optimization opportunity!
std::cout << (x / g.a); // Cannot optimize!
}
}
Because f
is a const object, and hence f.a
is a const object, the optimizer knows that f.a
has value 1 when it's used at the end of the function. It could, if it chose, optimize away the division. It doesn't know the same thing about g.a
: g
is not a const object, a pointer to it has been passed into unknown code, so its value might have changed. So if you're the author of nobody_knows_what_this_does1
or nobody_knows_what_this_does2
, and you're thinking of const_cast
ing p
and using it to modify its referand, then you can only do it if you somehow know that the referand is non-const. Which normally you don't, so normally you don't use const_cast
.