5

当我希望类的成员变量在类的生命周期内保持不变时,我有时会使用 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 对象后对其进行变异基本上是非法的。

有人可以澄清吗?这是一个不好的/未定义的行为/要做的事情吗?

4

2 回答 2

7

这是未定义的行为。根据 C++11 标准的第 7.1.6.1/4 段:

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

在这种情况下,您似乎希望您的对象在构造后“变为”常量。这是不可能的。

如果你vector的意思是const,你应该在构造函数的初始化列表中初始化它:

qqq(vector<foo>& other) 
    : my_foo(std::move(other)) 
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}

请注意,除非您有充分的理由通过指针传递 - 在这种情况下,您还应该检查指针是否非空 - 您应该考虑通过引用传递(如上所示),这是常见的做法。

更新:

正如 Pete Becker 在评论中正确指出的那样,正确的设计将表明从vector参数移动的决定应该属于 ' 的构造函数的调用者qqq而不是构造函数本身。

如果构造函数总是应该从它的参数中移动,那么你可以让它接受一个右值引用,明确构造函数本身对调用者的期望:

qqq(vector<foo>&& other) 
//             ^^
    : my_foo(std::move(other)) 
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}

这样,调用者必须在' 的构造函数的输入中提供一个右值:qqq

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
于 2013-04-18T17:43:33.750 回答
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
  }
};
于 2013-04-18T17:45:13.500 回答