这段代码颠覆了C ++类型系统?(Does this code subvert the C++ ty

2019-07-30 00:28发布

据我所知,具有const在C ++方法意味着对象是只读的通过的方法,但它仍然可以改变否则。

但是,此代码显然通过改变一个物体const (通过参照即const方法)。

这是法律的代码在​​C ++?

如果是这样的:是不是打破了const类型系统的-ness? 为什么/为什么不呢?

如果没有:为什么不呢?

注1:我已编辑的例子一点,这样的答案可能是指过去的一些例子。

编辑2:显然,你甚至不需要C ++ 11,所以我删除了依赖。

#include <iostream>

using namespace std;

struct DoBadThings { int *p; void oops() const { ++*p; } };

struct BreakConst
{
    int n;
    DoBadThings bad;
    BreakConst() { n = 0; bad.p = &n; } 
    void oops() const { bad.oops(); }  // can't change itself... or can it?
};

int main()
{
    const BreakConst bc;
    cout << bc.n << endl;   // 0
    bc.oops();              // O:)
    cout << bc.n << endl;   // 1

    return 0;
}

更新:

我已经迁移的lambda来构造函数的初始化列表,因为这样做可以让我随后说const BreakConst bc; ,其中-因为bc 本身现在是const的(而不是仅仅指针) -似乎暗示( 由斯特劳斯 ),修改bc在施工后任何方式应该导致未定义的行为,即使构造函数和调用者将有没办法知道这没有看到对方的定义。

Answer 1:

的糟糕()方法不允许改变对象的常量性。 此外,它不这样做。 它的匿名函数,做它。 这个匿名功能不在对象的上下文,但是在main()方法被允许修改对象的上下文中。

您的匿名函数不改变糟糕()(其被定义为常量,因此不能被改变),并且还以任何方式导出从这个这个指针一些非const变量的这个指针。 本身不具有任何此指针。 它只是忽略了这个指针和改变主上下文的BC变量(如参数类型的传递给你的闭合)。 这个变量不常量,因此可以改变。 您还可以通过任何匿名函数变化一个完全无关的对象。 此函数不知道,它改变存储它的对象。

如果你将它声明为

const BreakConst bc = ...

然后主要功能也将处理它为const对象,无法改变它。

编辑: 换句话说:const属性绑定到混凝土-1-值(基准)访问的对象。 它不绑定到对象本身。



Answer 2:

你的代码是正确的,因为你不使用const参考修改对象 。 拉姆达函数使用完全不同的参考,这恰好是指向同一个对象。

在一般情况下,这种情况下不颠覆式系统,因为在C ++类型系统没有正式保证,你不能修改const对象或常量引用。 然而,const对象的修饰是未定义的行为。

从[7.1.6.1]的cv修饰符

的指针或引用到CV-合格音响ED型不必实际上指向或引用的cv-合格音响编对象,但是,如果它确实它被处理; 一个const-合格音响ED访问路径不能被用来修改即使引用的对象是一个非const对象的对象,并且可以是通过一些其它访问路径MODI音响编辑。

除了任何类成员声明可变(7.1.1)可以是MODI音响版,任何企图其寿命(3.8)的结果中理解过程音响奈德行为期间修改const对象。



Answer 3:

我已经看到类似的东西。 基本上你调用一个调用别的,修改对象不知道它的成本函数。

认为这是很好:

#include <iostream>
using namespace std;

class B;

class A
{
    friend class B;
    B* pb;
    int val;
public:
    A(B& b); 
    void callinc() const;
    friend ostream& operator<<(ostream& s, const A& a)
    { return s << "A value is " << a.val; }
};

class B
{
    friend class A;
    A* pa;
public:
    void incval() const { ++pa->val; }
};

inline A::A(B& b) :pb(&b), val() { pb->pa = this; }
inline void A::callinc() const { pb->incval(); }


int main()
{
    B b;
    const A a(b);  // EDIT: WAS `A a(b)`
    cout << a << endl;
    a.callinc();
    cout << a << endl;
}

这不是C ++ 11,但不一样的:一点是常量不是传递

callinc()不改变自身aincval不会改变b 。 请注意,在main ,你甚至可以声明const A a(b); 代替A a(b); 一切编译相同。

此作品从几十年来,和你的样品中,你只是在做相同的:只需您更换B级与拉姆达。

编辑

改变的主要()来反映意见。



Answer 4:

问题是逻辑常量与按位常量之一。 编译器不知道你的程序的逻辑含义任何东西,只有强制执行按位常量。 它是由你来实现逻辑常量。 这意味着,在情况下,像你展示,如果指向存储在逻辑上是对象的一部分,你应该从一个const函数修改它避免,即使编译器将让你(因为它不是按位图像的一部分对象的)。 这也意味着,如果对象的按位图像的一部分,不是(例如嵌入式的引用计数,或缓存值)对象的逻辑值的一部分,你把它mutable ,甚至抛弃常量,在案件在那里你修改,而不会修改对象的逻辑值。



Answer 5:

const的功能仅仅是有助于防止意外的误操作。 它的目的不是要阻止专用软件的黑客。 这是一样的private和protected成员,别人随时都可以沿着存储器访问类的内部对象和增量的地址,也没有办法阻止它。

所以,是的,你可以避开常量。 如果不出意外,你可以简单地改变对象在内存的水平,但并不意味着常量被打破了。



文章来源: Does this code subvert the C++ type system?