Return unique_ptr 来自代理的成员变量 class with operator()

Return unique_ptr member variable from proxy class with operator()

我想制作一个包装器class(在本例中是非空检查,但也可能是其他检查)。

有没有什么方法可以通过operator()提取成员变量,从而可以出成员变量。用例是 std::unique_ptr<>

这是用例

#include <memory>

struct S {
    int x = 0;
    S(int x): x(x) {}
};

template <typename Type>
class NotNull
{
public:
    NotNull(Type value): m_value(std::move(value)) {}

    operator Type&() // <--------------------------------- this is the question
    {
       assertIsNotNull();
       return m_value;
    }


    typename std::pointer_traits<Type>::element_type *get() {
        return m_value.get();
    }

    private:
    void assertIsNotNull() {}

    Type m_value;
};

这就是需要的工作

// Test code
int main() {
    {
        NotNull<S *> x {new S{10}};
        auto y = x; // This works
        delete y;
    }

    {
        NotNull<std::shared_ptr<S>> x{std::make_shared<S>(10)};
        auto y = x; // This works
    }
        
    {
        NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
        S* y = x.get(); // This does work, and needs to work as expected
                        // that is _not_ move the member
    }

    {
        NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
        auto y = std::move(x); // This copies the whole class
    }

    {
        NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
        std::unique_ptr<S> y = std::move(x); // <----------------- This does not work
    }
}

编译器似乎不理解我想在 std::move 调用中转换为 unique_ptr。

error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = S; _Dp = std::default_delete<S>]'
   57 |         std::unique_ptr<S> y = std::move(x); 
      |     

Compiler explorer link

是否可以使用 class 作为 std::unique_ptr 的代理并获得这种移出成员变量的语法。

PS。正如您可能已经猜到的那样,我不能依赖 gsl::not_null,因为据我所知,该功能不存在。 DS.

编译器理解您想要转换为 unique_ptr&(即类型&)就好了。当您将该转换的结果分配给本地 unique_ptr 对象时,问题就出现了:由于它是左值引用,编译器会尝试调用复制构造函数(而不是移动构造函数,后者需要右值引用),但是由于unique_ptr删除了它的复制构造函数,你得到错误。

你可能想在该行中做的是转换为 unique_ptr&&(即右值引用)。为此,您可以根据 ref-qualifiers:

重载转换运算符
operator Type&() & // converts to lvalue ref if invoked on lvalue
{
   assertIsNotNull();
   return m_value;
}

operator Type&&() && // converts to rvalue ref if invoked on a temporary
{
   assertIsNotNull();
   return std::move(m_value);
}

这样,转换运算符将转换为调用它的相同类型的引用(即从普通变量使用的左值,如果用于临时或移动对象则为右值)。

您可以使用引用限定符根据对象是左值引用还是右值引用来创建成员函数的单独版本,如下所示:

#include <memory>
#include <iostream>

struct S {
    int x = 0;
    S(int x): x(x) {}
};

template <typename Type>
class NotNull
{
public:
    NotNull(Type value): m_value(std::move(value)) {}

    operator Type&()
    {
    assertIsNotNull();
    return m_value;
    }

    auto get() &
    {
        std::cout << "lvalue \n";
        return m_value.get();
    }

    auto get() &&
    {
        std::cout << "rvalue \n";
        auto retval =  m_value.get();
        m_value.release();
        return retval;
    }

    private:
    void assertIsNotNull() {}

    Type m_value;
};

int main()
{
    {
        NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
        S* y = x.get(); // This does work, and needs to work as expected
                        // that is _not_ move the member
    }
    {
        NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
        std::unique_ptr<S> y = std::unique_ptr<S>(std::move(x).get());  // Is this what you want?
    }
}