gpt4 book ai didi

c++ - 使用 unique_ptr 成员编写移动构造函数的正确方法(崩溃)

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:01:56 24 4
gpt4 key购买 nike

以下代码在Visual Studio 2013下会崩溃

我想知道为什么:在这种情况下编写移动构造函数的正确方法是什么?删除移动构造函数解决了这个问题。是 VC++ 的错误还是这段代码有误?

移动构造函数的默认定义有何不同,这使得这段代码不会崩溃,而我自己的定义会崩溃?

#include <memory>
#include <vector>

class A
{};

class Foo
{
public:
Foo(std::unique_ptr<A> ref) : mRef(std::move(ref)) {}
Foo(Foo&& other) : mRef(std::move(other.mRef)) {}

Foo(const Foo& other) {}
Foo& operator=(const Foo& other) { return *this; }

protected:
std::unique_ptr<A> mRef;
};

int main(int argc, char *argv[])
{
std::vector<Foo>({ Foo(std::make_unique<A>()), Foo(std::make_unique<A>()) });
// Crash : Debug Assertion Failed !
// Expression : _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
}

这可能与此有关,对吧?

Double delete in initializer_list vs 2013

这是充实了复制构造函数和赋值的实际代码,但错误完全相同

class A
{
public:
std::unique_ptr<A> clone() { return std::make_unique<A>(*this); }
};

class Foo
{
public:
Foo(std::unique_ptr<A> ref) : mRef(std::move(ref)) {}
Foo(Foo&& other) : mRef(std::move(other.mRef)) {}

Foo(const Foo& other) : mRef(other.mRef->clone()) {}
Foo& operator=(const Foo& other) { mRef = other.mRef->clone(); return *this; }

protected:
std::unique_ptr<A> mRef;
};

最佳答案

这听起来像是一个 VS-2013 错误。但看起来您的代码虽然格式正确,但可能也没有按照您的要求执行(但是只有您可以肯定地说)。

我在您的 Foo 中添加了打印语句:

class Foo
{
public:
Foo(std::unique_ptr<A> ref) : mRef(std::move(ref)) {}
Foo(Foo&& other) : mRef(std::move(other.mRef)) {}

Foo(const Foo& other) {}
Foo& operator=(const Foo& other) { return *this; }

friend std::ostream&
operator<<(std::ostream& os, const Foo& f)
{
if (f.mRef)
os << *f.mRef;
else
os << "nullptr";
return os;
}

protected:
std::unique_ptr<A> mRef;
};

然后我在你的 main 中添加了打印语句还有:


旁白:我还在你的 A 中添加了身份/状态这样就可以更轻松地了解正在发生的事情。


int main(int argc, char *argv[])
{
std::vector<Foo> v({ Foo(std::make_unique<A>(1)), Foo(std::make_unique<A>(2)) });
// Crash : Debug Assertion Failed !
// Expression : _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
for (const auto& p : v)
std::cout << p << '\n';
}

对我来说这个输出:

nullptr
nullptr

我认为这是正确的输出。

一个人不能initializer_list 移动,因此 vector构造函数调用 Foo的复制构造函数,它只是默认构造 unique_ptr .

确实,如果删除 Foo复制构造函数,然后应隐式删除(或者您可以显式删除它),程序不应编译。

要真正做到这一点,你必须给 Foo一个可操作的复制构造函数。也许是这样的:

    Foo(const Foo& other)
: mRef(other.mRef ? new A(*other.mRef) : nullptr)
{}

总而言之,我认为编译器和当前代码都因错误而获奖。尽管从评论来看,当前的代码错误听起来只是正确减少代码以隔离问题的产物。

VS-2013 错误。

至于你的移动构造函数,没问题。虽然如果用 = default 实现会更好.如果你这样做,它会自动继承一个 noexcept规范这样的规范不应该掉以轻心。最重要的是有效利用vector<Foo> .

我的理解是 VS-2013 既不理解默认移动成员也不理解 noexcept .

我对 VS-2013 的轶事经验是,编译器错误的数量与花括号的使用成正比。我希望 VS-2015 与这种体验相矛盾。同时,我建议避免使用涉及 {} 的构造表达式.


更新

您更新的拷贝成员位于:

class Foo
{
public:
Foo(std::unique_ptr<A> ref) : mRef(std::move(ref)) {}
Foo(Foo&& other) : mRef(std::move(other.mRef)) {}

Foo(const Foo& other) : mRef(other.mRef->clone()) {}
Foo& operator=(const Foo& other) { mRef = other.mRef->clone(); return *this; }

protected:
std::unique_ptr<A> mRef;
};

潜力 nullptr -取消引用错误。如果other处于移动状态,->clone()将取消引用 nullptr .从技术上讲,如果您非常非常小心,就可以摆脱这种情况。但是,您很容易不小心遇到此错误。

关于c++ - 使用 unique_ptr 成员编写移动构造函数的正确方法(崩溃),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28121973/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com