作为一个函数的参数,该函数将从一个派生类获取一个从一个基类获取一个从一个基类获取一个从一个派生类获取的惟一_ptr

我试图在一个函数中使用 unique_ptr来派生类,该函数将 unique_ptr作为基类。比如:

class Base {};


class Derived : public Base {};


void f(unique_ptr<Base> const &base) {}


…


unique_ptr<Derived> derived = unique_ptr<Derived>(new Derived);
f(derived);

如果我正确理解了 这个答案,这段代码应该可以工作,但是它会导致以下编译错误:

错误 C2664: ‘ f’: 无法将参数1从‘ std: : only _ ptr < _ Ty >’转换为‘ const std: : only _ ptr < _ Ty > &’

IntelliSense: 没有合适的用户定义转换,从“ std: : only _ ptr < Derived,std: : default _ delete < Derived > >”到“ const std: : only _ ptr < Base,std: : default _ delete < Base > >”不存在

如果我把 f改成 unique_ptr<Derived> const &derived,它工作得很好,但这不是我想要的。

我做错什么了吗? 我该怎么做才能解决这个问题?

我正在使用 Visual Studio 2012。

88241 次浏览

You have three options:

  1. Give up ownership. This will leave your local variable without access to the dynamic object after the function call; the object has been transferred to the callee:

    f(std::move(derived));
    
  2. Change the signature of f:

    void f(std::unique_ptr<Derived> const &);
    
  3. Change the type of your variable:

    std::unique_ptr<base> derived = std::unique_ptr<Derived>(new Derived);
    

    Or of course just:

    std::unique_ptr<base> derived(new Derived);
    

    Or even:

    std::unique_ptr<base> derived = std::make_unique<Derived>();
    
  4. Update: Or, as recommended in the comments, don't transfer ownership at all:

    void f(Base & b);
    
    
    f(*derived);
    

A possibile solution is to change the type of the argument to be a Base const*, and pass derived.get() instead. There is no transfer of ownership with unique_ptr const<Base>& (and the unique_ptr is not being modified), so changing to a Base const* does not change the meaning.


Herb Sutter discusses passing smart pointer arguments at length in Smart Pointer Parameters. An extract from the linked article refers to this exact situation:

Passing a const unique_ptr<widget>& is strange because it can accept only either null or a widget whose lifetime happens to be managed in the calling code via a unique_ptr, and the callee generally shouldn’t care about the caller’s lifetime management choice. Passing widget* covers a strict superset of these cases and can accept “null or a widget” regardless of the lifetime policy the caller happens to be using.

I had option #1 of the accepted answer and I still had the same compile error. I banged my head on the wall for over an hour and I finally realized that I had

class Derived : Base {};

instead of

class Derived : public Base {};

Another way is change the signature of f and use it in a slightly different way:

void f(Base* base_ptr) {
// take ownership inside the function
std::unique_ptr<Base> base {base_ptr};
// ...
}


// ...
auto derived = std::make_unique<Derived>();
f(derived.release());  // release ownership