c++ 此间接常量访问是否为UB?

hfyxw5xn  于 2022-11-19  发布在  其他
关注(0)|答案(1)|浏览(171)

当我今天检查一些代码的时候,我注意到一个实现std::enable_shared_from_this的老方法,它在构造函数中保留一个std::weak_ptr to self。

struct X {
    static auto create() {
        auto ret = std::shared_ptr<X>(new X);
        ret->m_weak = ret;
        return ret;
    }

    // use m_weak.lock() to access the object
    //... 
private:
    X() {}
    std::weak_ptr<X> m_weak;
};

但是我突然想到这个对象的constness。检查下面的代码:

struct X {
    static auto create() {
        auto ret = std::shared_ptr<X>(new X);
        ret->m_weak = ret;
        return ret;
    }

    void indirectUpdate() const {
        m_weak.lock()->val = 1;
    }

    void print() const {
        std::cout << val << '\n';
    }

private:
    X() {}
    std::weak_ptr<X> m_weak;
    int val = 0;
};

int main() {
    auto x = X::create();
    x->print();
    x->indirectUpdate();
    x->print();
}

在这段代码中,indirectUpdate()是一个常量方法,它不应该更新我们的对象,但事实上它确实是这样的。因为std::weak_ptr.lock()返回一个非常数shared_ptr<>,即使方法是const。所以你可以在const方法中间接更新你的对象。这在std::enable_shared_from_this的情况下不会发生,因为shared_from_this返回一个指向const ref的共享指针。我想知道这段代码是不是UB。我觉得应该是,但我不确定。有什么想法吗?

更新日期:

对不起,我的问题似乎没有被正确地传达。我的意思是,即使我们有一个常量指针,我们也会通过这个方法失去常量性。下面的代码显示:

struct X {
    static auto create() {
        auto ret = std::shared_ptr<X>(new X);
        ret->m_weak = ret;
        return ret;
    }

    void show() const { std::cout << "const \n";}
    void show() { std::cout << "non-const\n";}

    void indirectUpdate() const {
        show();
        m_weak.lock()->show();
        m_weak.lock()->val = 1;
    }

    void print() const {
        std::cout << val << '\n';
    }

    int val = 0;

private:
    X() {}
    std::weak_ptr<X> m_weak;
};

int main() {
    // Here we have a const pointer
    std::shared_ptr<const X> x = X::create();
    x->print();
    x->indirectUpdate();
    x->print();
}

输出如下:

0
const 
non-const
1

表现为失稳。

lvjbypge

lvjbypge1#

修改的对象不是const。没有未定义的行为。
添加如下方法:

#include <memory>
#include <iostream>

struct X {
    static auto create() {
        auto ret = std::shared_ptr<X>(new X);
        ret->m_weak = ret;
        return ret;
    }

    void show() const { std::cout << "const \n";}
    void show() { std::cout << "non-const\n";}

    void indirectUpdate() const {
        m_weak.lock()->show();
        m_weak.lock()->val = 1;
    }

    void print() const {
        std::cout << val << '\n';
    }

private:
    X() {}
    std::weak_ptr<X> m_weak;
    int val = 0;
};

int main() {
    auto x = X::create();
    x->print();
    x->indirectUpdate();
    x->print();
}

要获得此输出,请执行以下操作:

0
non-const
1

通过const方法修改对象是可以的,只要该方法只修改实际上不是const的对象。
这类似于对一个非常量对象使用const &,你可以抛弃constness并修改它,只要该对象真的不是const:

#include <iostream>

int main() {
    int x = 0;
    const int& ref = x;
    const_cast<int&>(ref) = 42;
    std::cout << x;
}

我也没有看到在代码中误用模式的危险,因为一旦对象是const,就不能给它的val成员赋值了,而使用constcorrection就可以了。
在您的更新中,您在main中有一个const指针,但对象仍然不是const。请考虑以下更简单的示例:

#include <iostream>

struct foo {
    static foo* create(){
        auto x = new foo();
        x->self = x;
        return x;
    }

    void bar() const {
        this->self->non_const();
    }
    void non_const() {
        std::cout << "Hello World\n";
    }
    foo* self;
};

int main() {
    const foo* f = foo::create();
    f->bar();
    delete f;
}

它和你的不太一样,但是它有类似的效果,在一个看似const的对象上调用一个非常量的方法。
唯一的foo对象是在foo::create中创建的,它不是常量。在main中,我们有一个指向该对象的const foo*main只能调用const成员bar。在bar中,this指针是一个const foo*,但self并不指向const fooself本身是'const,但不是它所指向的对象。

相关问题