class WeirdString {
private:
char* buffer;
size_t length;
size_t capacity;
bool owns;
public:
// Non-owning constructor
WeirdString(char* buffer, size_t length, size_t capacity)
: buffer(buffer), length(length), capacity(capacity), owns(false)
{ }
// Make an owning copy
WeirdString(WeirdString const& rhs)
: buffer(new char[rhs.capacity])
, length(rhs.length)
, capacity(rhs.capacity)
, owns(true)
{
memcpy(buffer, rhs.buffer, length);
}
~WeirdString() {
if (owns) delete [] buffer;
}
};
那个复制构造函数在标准中是否有违规之处?考虑如下:
WeirdString get(); // this returns non-owning string
const auto s = WeirdString(get());
s
是拥有或非拥有的,这取决于额外的复制构造函数是否被省略,而在C++14及之前,这是允许但是可选的(尽管在C++17中是保证的)。这种薛定谔所有权模型表明,这个复制构造函数本身是未定义行为。
它是吗?
一个更形象的例子可能是:
struct X {
int i;
X(int i)
: i(i)
{ }
X(X const& rhs)
: i(rhs.i + 1)
{ } ~~~~
};
X getX();
const auto x = X(getX());
根据省略的副本,x.i
可能比 getX()
返回的值多0、1或2。标准文件有关于此的规定吗?
f() + g()
表达式中,不确定是先调用f
还是g
,这本身并不足以声明该表达式具有未定义的行为。当然可能存在一种情况,即g
某种程度上依赖于由f
产生的副作用,在没有副作用的情况下会出现未定义的行为。你所面临的情况类似:复制构造函数可能会被省略,从而导致得到拥有或非拥有实例——这本身不会引发未定义的行为;但是可能存在某些进一步的操作依赖于实例处于特定状态,并且在状态不符时出现问题。 - Igor Tandetnik