我刚刚发现在没有任何const_cast黑魔法的情况下修改const对象是多么容易.考虑:
#include <iostream>
class Test {
public:
Test(int v)
:m_val{ v },
m_ptr{ &m_val }
{}
int get() const { return m_val; }
void set(int v) const { *m_ptr = v; }
private:
int m_val;
int* m_ptr;
};
int main()
{
const Test t{ 10 };
std::cout << t.get() << '\n';
t.set(0);
std::cout << t.get() << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
最近版本的Clang,GCC和MSVC没有显示任何警告并产生预期输出:
10 0
这是根据现行标准定义良好的行为吗?如果它未定义什么,如果m_val是类型std::aligned_storage_t<sizeof(int), alignof(int)>和构造new"版int的呢?我相信在小缓冲区优化方面这是很常见的情况.
编辑
谢谢,看起来这只是另一种用脚射击自己的方式.令人不安的是,这似乎是:
struct Test2 {
int i;
void operator()() { ++i; }
};
const std::function<void()> f{ Test2{ 10 } };
f();
Run Code Online (Sandbox Code Playgroud)
当实现选择将Test2对象存储在内部时f(在libc ++和Visual Studio中就是这种情况),也是未定义的行为
const 强制执行"按位常量",但你通常想要的是"逻辑常量".
对于包含指针的对象,这意味着const成员函数不能修改指针本身,但可以修改指针引用的内容.
这在很长一段时间内都是众所周知的.
要获得逻辑常量,您1)使用mutable(或有时const_cast)允许修改不影响对象逻辑状态的成员(例如,缓存值/ memoization),以及2)通常必须手动强制不通过a写入数据指针(但如果它是一个拥有指针,那么所有权应该委托给一个只管理该数据所有权的对象,在这种情况下,使它成为常常应该阻止写入它拥有的数据).
至于具有指向可能本身已被修改的数据的非常量指针的具体细节,那么,你基本上只是得到一个与const_cast通常用来做的大致相同的(持久)版本:get非const访问您只有const指针的数据.由你来确保你只以不会引起问题的方式使用它(但只是通过和/或通过指针写入本身不一定会导致问题).
换句话说,我们这里有两个单独指向某些数据的指针.this允许您访问对象的数据.在const成员函数中,this除了(如上所述)标记之外,您只能读取(不)读取数据mutable.在这种情况下,您将保存指向同一数据的第二个指针.因为没有什么可以将它标记为指针const,所以它不是,所以你得到它所指向的数据的非const访问.