std::list< std::unique_ptr<T> >:如何传递它

11

假设我有一个std::list,其中包含class T的对象:

std::list<T> l;

当将其传递给函数时,我会使用引用:

someFunction( std::list<T> &l )

如何最好地传递(元素)一个std::listunique_ptr

std::list< std::unique_ptr<T> >  l;

就像这样:

someFunction( std::unique_ptr<T> ptr )

或者这个:
someFunction( T* ptr )

或者这个:
someFunction( T &ref )

如果使用 std::listback() 函数,我该如何称呼它?这些在我看来都是“类似”的,但我肯定漏掉了什么。

谢谢。

3个回答

9
按照从好到差的顺序依次排列:
  1. someFunction(const T&);
  2. someFunction(T&);
  3. someFunction(const std::unique_ptr<T>&&);
  4. someFunction(std::unique_ptr<T>&&)。
第一个是最好的,因为它不会修改对象,并且无论你如何分配对象,它都能使用它(例如,你可以毫不费力地切换到 shared_ptr)。
第二个也能够使用任何智能指针,但它假设你可以修改对象,而当你可以将某些东西设为 const 时,你就应该这样做。
第三和第四两者允许所指向的对象被改变,然而 #3 不允许智能指针被更改,而 #4 则可以。两者的劣势在于它们强制使用 unique_ptr,而前面两个则无论智能指针类型如何都能够正常工作。
像其他示例中一样以值传递 unique_ptr 不是一个选项;unique_ptr 应该是唯一的。如果你需要拷贝它,请考虑使用 shared_ptr。
对于前两个,如果你将其应用于 back() 的结果,它将看起来像:
someFunction(*(lst.back()));  // dereference lst.back() before passing it in.

对于后两种情况,如果您在back()的结果上调用它,它将如下所示:
someFunction(lst.back()); // pass the smart pointer, not the object to
                          // which the smart pointer currently points.

感谢您的深入解释。因此,智能指针不需要特殊的语义(除了解引用)。我知道const的东西,但在这种情况下,我需要修改传递进来的T对象。 - rubenvb

2
不要通过值传递unique_ptr,首先它不会编译而不使用std::move,如果您使用了std::move,它会清空您在list中存储的值,您将无法再访问它。
这是因为unique_ptr不可复制,它没有类型为unique_ptr::unique_ptr(const unique_ptr<T>& other)的复制构造函数,而只有一个移动构造函数(unique_ptr::unique_ptr(unique_ptr<T>&& source))。

我从相反的需求得到了这里:如何将unique_ptr从列表中弹出,因为当前的API需要复制(auto res = list.back(); list.pop_back(); return res;),所以你回答了我的问题:将值从back指针中移动,然后弹出空指针。 - Guss

0

unique_ptr以及包含unique_ptr的类/实例可以用于std::list(和其他容器),前提是它们定义了移动构造函数 class_name(class_name &&)(unique_ptr当然已经有了)。

当你传递这些元素时,你总是在移动(而不是复制)它们,所以你总是在lvalues上使用std::move(),就像这样:
my_list.push_back(std::move(my_element));
这使得你传递(=移动)元素到列表中,并且在该操作之后my_element就像空的unique_ptr一样“为空”。

示例:

typedef std::unique_ptr<uint8_t[]> data_ptr;

class data_holder
{
private:
    int something_about_data;
    data_ptr data;
public:
    data_holder(data_ptr && _data)
        : data(std::move(_data))
    {}

    // hey compiler, please generate a default move constructor for me
    // despite of present destructor
    data_holder(data_holder &&) = default;

    ~data_holder()
    {
        // ...
    }

    bool is_empty() const { return ! bool(data); }
}

// ...
{
    // ...
    data_holder the_element(data_ptr(new uint8_t[DATA_SIZE]));

    // move the_element into the_list
    the_list.push_back(std::move(the_element));
    if (the_element.is_empty())
        std::cerr << "data_holder 'the_element' is now empty!" << std::endl;
    // ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接