通过基类接受方法转发派生 UniquePtr 的右值会移动引用而不是复制

Forwarding Rvalue Of Derived UniquePtr Through Base Class Accepting Method Moves Reference Instead of Copying

本文关键字:移动 引用 复制 基类 方法 UniquePtr 派生 转发      更新时间:2023-10-16

似乎将右值引用转发到std::unique_ptr<Derived>到接受方法,const std::unique_ptr&基类会导致 std::unique_ptr 被移出。对于数字或非继承类,不会发生这种情况。

最初是在MSVC++ 2019上发现的,但也在各种其他编译器上进行了测试,这些编译器都给出了相同的结果。这种行为是否符合标准?

#include <memory>
#include <cassert>
#include <iostream>
class Base
{};
class Derived : public Base
{};
class NonInheriting
{};
void Corrupter(const std::unique_ptr<Base>&)
{}
void Corrupter(const std::unique_ptr<int>&)
{}
void Corrupter(const std::unique_ptr<NonInheriting>&)
{}
void Usage(std::unique_ptr<Base>&& base)
{
assert(base);
}
void Usage(std::unique_ptr<int>&& base)
{
assert(base);
}
void Usage(std::unique_ptr<NonInheriting>&& base)
{
assert(base);
}
template<class... Args>
void Forwarder(Args&& ... args)
{
Corrupter(std::forward<Args>(args)...);
Usage(std::forward<Args>(args)...);
}
int main()
{
// No assertion
std::cout << "1n";
auto integer = std::make_unique<int>();
Forwarder(std::move(integer));
// No assertion
std::cout << "2n";
auto nonInheriting = std::make_unique<NonInheriting>();
Forwarder(std::move(nonInheriting));
// No assertion
std::cout << "3n";
std::unique_ptr<Base> base = std::make_unique<Derived>();
Forwarder(std::move(base));
// Assertion
std::cout << "4n";
auto derived = std::make_unique<Derived>();
Forwarder(std::move(derived));
return 0;
}

这是预期行为。

对于第 4 种情况,您传递了一个std::unique_ptr<Derived>Forwarder它传递给Corrupter,后者期望std::unique_ptr<Base>std::unique_ptr<Derived>可以隐式转换为std::unique_ptr<Base>,然后将参数args转换为std::unique_ptr<Base>,指针的所有权也转移到临时std::unique_ptr<Base>args现在一无所有。之后,它被传递给Usage然后触发断言。

在其他情况下,没有发生转换和所有权转让,那么他们就没有这样的问题。

如果您添加另一个Corrupter的重载,std::unique_ptr<Derived>,即消除转换和所有权转让,则没有断言。