4

下面的代码是不好的做法还是未定义的行为?本质上,我正在调用 const func 来修改未标记为可变的成员。演示链接

感谢 Mehrdad 对这个问题的启发(他的问题Do this code subvert the C++ type system?)和 david 对演示的微小改进。

#include <iostream>
using namespace std;

struct BreakConst
{
    int v;
    int *p;
    BreakConst() { v = 0; p = &v; } 
    void break_stuff() const { ++*p; }
};
void f(const BreakConst& bc) {
    bc.break_stuff();
}

大多数答案基于的原始版本:

David Rodríguez、jpalecek、Mehrdad 回答
是:这是“未定义的行为”

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

    return 0;
}

新的替代问题:

Mehrdad 回答
否:这不是“未定义的行为”

int main()
{
    BreakConst bc;
    cout << bc.v << endl;   // 0
    f(bc);                  // O:)
    cout << bc.v << endl;   // 1

    return 0;
}

结果:

0
1
4

3 回答 3

3

在这种情况下,我会说这是未定义的行为。bc是一个const对象,它的所有子对象(少mutables)1也是一个对象,因此也bc.v应该是,并且修改一个const对象,无论如何实现,都是 UB 2

[1] C++03 3.9.3/3:

const 限定的类对象的每个非静态、非可变、非引用数据成员都是 const 限定的...

[2] C++03 7.1.5.1/4:

除了可以修改任何声明为 mutable (7.1.1) 的类成员外,任何在 const 对象的生命周期 (3.8) 期间修改它的尝试都会导致未定义的行为。

编辑回应问题的编辑:不,代码的修改版本不会导致未定义的行为。这可能是不好的做法,但实际上有时可能很有用。你可以例如。使用它通过 const-iterators (DRY) 实现类的迭代器:

class const_iterator
{
public:
  const T& dereference() const; // complicated
};

class iterator : public const_iterator
{
public:
  T& dereference() const { return const_cast<T&>(const_iterator::dereference()); }
};

当然,这依赖于iterators 只能由可变容器制成,const 和非 const 版本没有区别(没有 COW 等)等事实,但这是相当普遍的。

于 2012-06-18T21:35:55.127 回答
3

在您的特定情况下,它是未定义的行为,因为对象const,而不仅仅是引用。在以下情况下,这将是不好的做法(并且危险地接近未定义的行为):

void f( const BreakConst& b ) {
   bc.break_stuff();
}
int main() {
   BreakConst b;
   f( b );
}

不同之处在于,在这种情况下,实际对象不是 const,即使级别的引用f是。危险地接近未定义的行为来自这样一个事实,即丢弃 const-ness 的成员函数不可能知道调用它的对象是否为 const,因此您失去了所有控制权。

于 2012-06-18T21:37:18.227 回答
2

编辑后

,它不是未定义的。您可以通过 const 引用修改可变对象;这是完全允许和合法的。


您编辑之前:

是的,它必须是未定义的,因为标准(我在这里查看草稿)清楚地表明:

§7.1.6.1.4

mutable除了可以修改声明的任何类成员(第 77.1.1 节)外,任何在const对象生命周期内(第 3.8 节)修改对象的尝试都会导致未定义的行为。

所以是的——因为成员不是可变的,修改它显然是未定义的行为。

我不知道为什么规则是这样的,这是否是故意的,是否确实是一个漏洞,是否也违反了另一个规则,你应该怎么看它,等等......但是关于是否是UB的问题:是的,根据标准它是未定义的。

于 2012-06-18T21:45:43.283 回答