当我希望类的成员变量在类的生命周期中保持不变时,我有时会使用const_cast,但它在构造函数中需要是可变的。例如:
struct qqq {
const vector<foo> my_foo;
qqq(vector<foo>* other) {
vector<foo>& mutable_foo = const_cast<vector<foo>&>(my_foo)
other->swap(mutable_foo);
}
};
我曾经假设在构造函数中执行此操作基本上没问题,因为此时没有其他人依赖它,所以它不会与优化等交互不良。
然而,最近有人告诉我这是“未定义的行为”,并且在任何情况下构建const对象之后改变const对象基本上是非法的。
有人可以澄清吗?这是一个糟糕/未定义的行为/事情吗?
答案 0 :(得分:5)
这是未定义的行为。根据C ++ 11标准的第7.1.6.1/4段:
除了可以修改声明
mutable
(7.1.1)的任何类成员之外,任何修改const
的尝试都是如此 对象在其生命周期(3.8)中导致未定义的行为。
在这种情况下,您似乎希望您的对象在构造后“变得”不变。这是不可能的。
如果您的vector
是const
,则应在构造函数initialization list中对其进行初始化:
qqq(vector<foo>& other)
: my_foo(std::move(other))
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}
注意,除非你有充分的理由通过指针传递 - 在这种情况下,你还应该检查指针是否为非空 - 你应该考虑通过引用传递(如上所示),这是常见的实践。
<强>更新强>
正如Pete Becker在评论中正确指出的那样,正确的设计会表明从vector
参数移出的决定应属于qqq
的调用者构造函数,而不是构造函数本身。
如果构造函数总是应该从它的参数移开,那么你可以让它接受一个右值引用,明确构造函数本身对调用者的期望:
qqq(vector<foo>&& other)
// ^^
: my_foo(std::move(other))
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}
这样,调用者必须在qqq
的构造函数的输入中提供 rvalue :
std::vector<foo> v;
// ...
qqq q1(v); // ERROR!
qqq q2(std::move(v)); // OK! Now the client is aware that v must be moved from
答案 1 :(得分:2)
是的,它确实是UB(未定义的行为)。初始化后,您无法修改const
对象。你应该做的是使用成员初始化列表,可能还有一个函数:
struct qqq {
const vector<foo> my_foo;
qqq(vector<foo> *other) : my_foo(initialiseFoo(*other)) {}
static vector<foo> initialiseFoo(vector<foo> &other) {
vector<foo> tmp;
other.swap(tmp);
return tmp;
}
};
一个体面的优化者应该能够摆脱暂时的。
如果你可以使用C ++ 11,它实际上更简单:
struct qqq {
const vector<foo> my_foo;
qqq(vector<foo> *other) : my_foo(std::move(*other))
{
other->clear(); //Just in case the implementation of moving vectors is really weird
}
};