移动构造函数不是继承的,也不是默认生成的
Move constructor not inherited nor default generated
我尝试用一个函数扩展 std::ifstream
以便更容易读取二进制变量,令我惊讶的是,using std::ifstream::ifstream;
移动构造函数没有被继承。更糟糕的是,它被明确删除。
#include <fstream>
class BinFile: public std::ifstream
{
public:
using std::ifstream::ifstream;
//BinFile(BinFile&&) = default; // <- compilation warning: Explicitly defaulted move constructor is implicitly deleted
template<typename T>
bool read_binary(T* var, std::streamsize nmemb = 1)
{
const std::streamsize count = nmemb * sizeof *var;
read(reinterpret_cast<char*>(var), count);
return gcount() == count;
}
};
auto f()
{
std::ifstream ret("some file"); // Works!
//BinFile ret("some file"); // <- compilation error: Call to implicitly-deleted copy constructor of 'BinFile'
return ret;
}
我不想显式实现移动构造函数,因为它感觉不对。问题:
- 为什么被删了?
- 删除它有意义吗?
- 有没有办法修复我的 class 以便正确继承移动构造函数?
问题是 basic_istream
(basic_ifstream
的基础,其中模板 ifstream
是一个实例)实际上 继承自 basic_ios
,并且 basic_ios
有一个删除的移动构造函数(除了受保护的默认构造函数)。
(虚拟继承的原因是fstream
的继承树中有一颗菱形继承自ifstream
和ofstream
。)
鲜为人知 and/or 很容易被遗忘的事实是,最派生的 class 构造函数直接调用其(继承的)虚拟基类构造函数,如果它没有在基类或-member-init-list 然后将调用虚拟基的 default 构造函数。但是(这更加模糊),对于隐式定义或声明为默认的 copy/move 构造函数,选择的虚拟基础 class 构造函数是 而不是 默认构造函数但是是对应的copy/move构造函数;如果它被删除或不可访问,最派生的 class copy/move 构造函数将被定义为已删除。
这是一个示例(最早可追溯到 C++98):
struct B { B(); B(int); private: B(B const&); };
struct C : virtual B { C(C const&) : B(42) {} };
struct D : C {
// D(D const& d) : C(d) {}
};
D f(D const& d) { return d; } // fails
(这里B
对应basic_ios
,C
对应ifstream
,D
对应你的BinFile
;basic_istream
对于演示来说是不必要的。)
如果取消注释 D 的手动复制构造函数,程序将编译但会调用 B::B()
, 而不是 B::B(int)
。这就是为什么从 classes 继承一个没有明确授予您这样做的权限的坏主意的原因之一;如果该构造函数被称为最派生的 class 构造函数,则您可能不会调用您正在继承的 class 的构造函数调用的相同虚拟基构造函数。
至于你能做什么,我相信手写的移动构造函数应该可以工作,因为在 libstdc++ 和 libcxx 中,basic_ifstream
的移动构造函数不会调用 [= 的非默认构造函数14=](有一个,来自 basic_streambuf
指针),而是在构造函数主体中初始化它(看起来这就是 [ifstream.cons]/4 is saying). It would be worth reading Extending the C++ Standard Library by inheritance? 其他潜在问题的原因。
正如前面的回答提到的,构造函数被定义为在基 class 中删除。
这意味着,您不能使用它 - 请参阅 <istream>
:
__CLR_OR_THIS_CALL basic_istream(const basic_istream&) = delete;
basic_istream& __CLR_OR_THIS_CALL operator=(const basic_istream&) = delete;
并且 return ret
尝试使用已删除的复制构造函数而不是移动构造函数。
但是,如果您创建自己的移动构造函数,它应该可以工作:
BinFile(BinFile&& other) : std::ifstream(std::move(other))
{
}
您可以在您的问题 (@igor-tandetnik) 的评论之一中看到这一点。
我尝试用一个函数扩展 std::ifstream
以便更容易读取二进制变量,令我惊讶的是,using std::ifstream::ifstream;
移动构造函数没有被继承。更糟糕的是,它被明确删除。
#include <fstream>
class BinFile: public std::ifstream
{
public:
using std::ifstream::ifstream;
//BinFile(BinFile&&) = default; // <- compilation warning: Explicitly defaulted move constructor is implicitly deleted
template<typename T>
bool read_binary(T* var, std::streamsize nmemb = 1)
{
const std::streamsize count = nmemb * sizeof *var;
read(reinterpret_cast<char*>(var), count);
return gcount() == count;
}
};
auto f()
{
std::ifstream ret("some file"); // Works!
//BinFile ret("some file"); // <- compilation error: Call to implicitly-deleted copy constructor of 'BinFile'
return ret;
}
我不想显式实现移动构造函数,因为它感觉不对。问题:
- 为什么被删了?
- 删除它有意义吗?
- 有没有办法修复我的 class 以便正确继承移动构造函数?
问题是 basic_istream
(basic_ifstream
的基础,其中模板 ifstream
是一个实例)实际上 继承自 basic_ios
,并且 basic_ios
有一个删除的移动构造函数(除了受保护的默认构造函数)。
(虚拟继承的原因是fstream
的继承树中有一颗菱形继承自ifstream
和ofstream
。)
鲜为人知 and/or 很容易被遗忘的事实是,最派生的 class 构造函数直接调用其(继承的)虚拟基类构造函数,如果它没有在基类或-member-init-list 然后将调用虚拟基的 default 构造函数。但是(这更加模糊),对于隐式定义或声明为默认的 copy/move 构造函数,选择的虚拟基础 class 构造函数是 而不是 默认构造函数但是是对应的copy/move构造函数;如果它被删除或不可访问,最派生的 class copy/move 构造函数将被定义为已删除。
这是一个示例(最早可追溯到 C++98):
struct B { B(); B(int); private: B(B const&); };
struct C : virtual B { C(C const&) : B(42) {} };
struct D : C {
// D(D const& d) : C(d) {}
};
D f(D const& d) { return d; } // fails
(这里B
对应basic_ios
,C
对应ifstream
,D
对应你的BinFile
;basic_istream
对于演示来说是不必要的。)
如果取消注释 D 的手动复制构造函数,程序将编译但会调用 B::B()
, 而不是 B::B(int)
。这就是为什么从 classes 继承一个没有明确授予您这样做的权限的坏主意的原因之一;如果该构造函数被称为最派生的 class 构造函数,则您可能不会调用您正在继承的 class 的构造函数调用的相同虚拟基构造函数。
至于你能做什么,我相信手写的移动构造函数应该可以工作,因为在 libstdc++ 和 libcxx 中,basic_ifstream
的移动构造函数不会调用 [= 的非默认构造函数14=](有一个,来自 basic_streambuf
指针),而是在构造函数主体中初始化它(看起来这就是 [ifstream.cons]/4 is saying). It would be worth reading Extending the C++ Standard Library by inheritance? 其他潜在问题的原因。
正如前面的回答提到的,构造函数被定义为在基 class 中删除。
这意味着,您不能使用它 - 请参阅 <istream>
:
__CLR_OR_THIS_CALL basic_istream(const basic_istream&) = delete; basic_istream& __CLR_OR_THIS_CALL operator=(const basic_istream&) = delete;
并且 return ret
尝试使用已删除的复制构造函数而不是移动构造函数。
但是,如果您创建自己的移动构造函数,它应该可以工作:
BinFile(BinFile&& other) : std::ifstream(std::move(other))
{
}
您可以在您的问题 (@igor-tandetnik) 的评论之一中看到这一点。