通过构造期间获得的指针修改const对象

cod*_*odi 10 c++ const

我刚刚发现在没有任何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中就是这种情况),也是未定义的行为

Jer*_*fin 7

const 强制执行"按位常量",但你通常想要的是"逻辑常量".

对于包含指针的对象,这意味着const成员函数不能修改指针本身,但可以修改指针引用的内容.

这在很长一段时间内都是众所周知的.

要获得逻辑常量,您1)使用mutable(或有时const_cast)允许修改不影响对象逻辑状态的成员(例如,缓存值/ memoization),以及2)通常必须手动强制不通过a写入数据指针(但如果它是一个拥有指针,那么所有权应该委托给一个管理该数据所有权的对象,在这种情况下,使它成为常常应该阻止写入它拥有的数据).

至于具有指向可能本身已被修改的数据的非常量指针的具体细节,那么,你基本上只是得到一个与const_cast通常用来做的大致相同的(持久)版本:get非const访问您只有const指针的数据.由你来确保你只以不会引起问题的方式使用它(但只是通过和/或通过指针写入本身不一定会导致问题).

换句话说,我们这里有两个单独指向某些数据的指针.this允许您访问对象的数据.在const成员函数中,this除了(如上所述)标记之外,您只能读取(不)读取数据mutable.在这种情况下,您将保存指向同一数据的第二个指针.因为没有什么可以将它标记为指针const,所以它不是,所以你得到它所指向的数据的非const访问.