2

N2812中是引言中的一个示例,其中 aunique_ptr作为值参数给出

void push_back2(
  std::list<std::unique_ptr<int>>& l, std::unique_ptr<int> a)
{
  l.push_back(a); // oops: moves from the lvalue 'a', silently!
  l.push_back(a); // oops: 'a' no longer has its original value
}

本文讨论了 RValue/LValue 重载解析的问题,但这不是我的意思。

我想知道,如果std::unique_ptr<int> a 按值提供参数不会导致编译器错误?它会复制它,对吗?这是不允许unique_ptr

我知道这篇论文已经很老了,也许从那以后,它的定义unique_ptr已经改变了。但也许这只是一个错字,作者想改写std::unique_ptr<int> &a

我的 gcc 4.7.0 同意我的观点,但这没有证据 :-)

void push_back2( std::list<std::unique_ptr<int>>&, std::unique_ptr<int> ) { };
int main() {
  list<unique_ptr<int>> lst;
  unique_ptr<int> num { new int{4} };
  push_back2(lst, num); //ERR: use of deleted function
}
4

3 回答 3

9

按值取参数没有错。您是正确的,如果您尝试使用副本初始化参数,您将收到编译器错误,因为该函数已被删除。但是,您可以通过提供右值作为参数来初始化 value 参数。例如:

std::unique_ptr<int> myPtr{ /* ... */ }
std::list<std::unique_ptr<int>> elems;
push_back2(elems, myPtr);            // Error, as you've noted
push_back2(elems, std::move(myPtr)); // Fine, uses move constructor to initialize

这种语法很好,因为它强制您明确指出您正在将指针交给函数。

一旦你在里面push_back2,你是正确push_back的,unique_ptr因为它会尝试使用不存在的复制构造函数。要解决此问题,您需要std::move再次使用:

void push_back2(
  std::list<std::unique_ptr<int>>& l, std::unique_ptr<int> a)
{
  l.push_back(std::move(a)); // Fine, moves a
  l.push_back(std::move(a)); // Well that was dumb, but you asked for it!
}

我希望我正确地解释了您的问题,并且这就是您要寻找的...如果还有什么我可以尝试澄清的,请告诉我!

于 2011-08-21T21:16:55.620 回答
3

您对行为的了解和假设是正确的。

该论文的示例令人困惑,因为它将两种语​​言混为一谈:带有概念的 C++ 和没有概念的 C++。在论文的假装语言中,require 的listpush_backCopyConstructible被 SFINAE 删除了,只留下了重载 requires MoveConstructible。在这样的list设计中,以及左值可以绑定到右值引用的旧规则,那么push_back将隐式移动a两次。

请注意,我们在任何时候list实际上都没有这样做的危险。作者只是试图建立一个没有超载的情况,const value_type&value_type&&你只有value_type&&在超载集中。

于 2011-08-21T21:36:50.700 回答
0

您可以通过右值提供它,例如,通过函数返回。

unique_ptr<int> make_unique() {
    return unique_ptr<int>(new int);
}
int main() {
    list<unique_ptr<int>> lst;
    lst.push_back(make_unique());
}

另外,你可以显式地std::move进入列表。

于 2011-08-21T21:17:51.787 回答