将派生类 unique_ptr 赋值给基类 unique_ptr

6
我创建了一个自定义的 istream,它派生自 std::istream,在文件是压缩文件时使用自定义的 streambuf,在其他情况下使用 std::filebuf。
#mystream.h
class my_stream: public istream {
  public:
     explicit my_stream(const std::string &path);
  private:       
     std::unique_ptr<std::streambuf> b_;
}

#mystream.cpp
my_stream::my_stream(const std::string &path) :std::istream(nullptr) {
    if(path.substr(path.length()-6, path.length())==".gzip"){
        b_ = std::make_unique<gzipbuf>(path); //gzipbuf is derived from std::streambuf
    } 
    else {
        std::unique_ptr<std::filebuf> fb;
        fb->open(path.c_str(), std::ios::in);
        b_ = fb;
    }
    this->init(b_.get());
}

我能够在一个地方将派生类的unique_ptr赋值给基类的unique_ptr

b_ = std::make_unique<gzipbuf>(path);

但不在另一个位置

b_ = fb;

它说:

candidate function not viable: no known conversion from 'unique_ptr<std::filebuf, default_delete<std::basic_filebuf<char>>>' to 'unique_ptr<std::basic_streambuf<char>, default_delete<std::basic_streambuf<char>>>' for 1st argument
      operator=(unique_ptr&& __u) noexcept

3
你需要进行移动操作:b = std::move(fb);,因为这里只能有一个对象(不能复制)。 - Galik
2个回答

7
首先,在这一行之后。
std::unique_ptr<std::filebuf> fb;

fb并没有指向任何东西,它只是一个空的unique_ptr,所以你在这里调用了未定义的行为:

fb->open(path.c_str(), std::ios::in);

为了解决这个问题,只需将该行更改为:
auto fb = std::make_unique<std::filebuf>();

关于您遇到的错误,如果允许此行,则
b_ = fb;

然后,b_fb都将指向同一个对象。这是unique_ptr不允许的。一个资源只能被一个unique_ptr拥有。一种解决方法是使用std::move将所有权从fb传递给b_

b_ = std::move(fb)

然后fb不再拥有任何东西。

个人而言,我喜欢在构造函数的初始化列表中尽可能地初始化成员变量,并会将创建streambuf的过程提取到一个单独的函数中以实现此目的:

std::unique_ptr<std::streambuf> createStream(const std::string &path) {
    if(path.substr(path.length()-5, path.length())==".gzip"){  // I think you meant 5 here!
        return std::make_unique<gzipbuf>(path); 
    }
    auto fb = std::make_unique<std::filebuf>();
    fb->open(path.c_str(), std::ios::in);
    return fb;
}

那么my_stream的构造函数可以是:

my_stream::my_stream(const std::string &path) : std::istream(nullptr),
  b_(createStream(path)) {
    this->init(b_.get());
}

谢谢!那很有道理。 - pikachu

2

我也曾经遇到过这种情况,但是那是因为我忘记了公开继承 :)

// Was:
class Derived: Base
{
};

// Should have been:
class Derived: public Base
{
};

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