首页 > 解决方案 > 通过基类接受方法转发派生的 UniquePtr 的右值移动引用而不是复制

问题描述

似乎将对 a 的右值引用转发给采用基类std::unique_ptr<Derived>的 a 的接受方法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 << "1\n";
    auto integer = std::make_unique<int>();
    Forwarder(std::move(integer));

    // No assertion
    std::cout << "2\n";
    auto nonInheriting = std::make_unique<NonInheriting>();
    Forwarder(std::move(nonInheriting));

    // No assertion
    std::cout << "3\n";
    std::unique_ptr<Base> base = std::make_unique<Derived>();
    Forwarder(std::move(base));

    // Assertion
    std::cout << "4\n";
    auto derived = std::make_unique<Derived>();
    Forwarder(std::move(derived));

    return 0;
}

标签: c++c++17

解决方案


这是预期的行为。

对于第 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然后断言被触发。

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

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

居住


推荐阅读