拡張してみましたstd::ifstream
バイナリ変数を読みやすくするための関数が1つあり、驚いたことにusing std::ifstream::ifstream;
moveコンストラクターは継承されません。さらに悪いことに、それは明示的に削除されます。
#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;
}
私はムーブコンストラクタを間違って感じているだけなので、明示的に実装したくありません。質問:
問題は、_basic_istream
_(_basic_ifstream
_のベースであり、テンプレートifstream
はインスタンス化です)virtuallyは_basic_ios
_から継承し、_basic_ios
_には(保護されたデフォルトコンストラクターに加えて)移動コンストラクターが削除されています。
(仮想継承の理由は、fstream
とifstream
から継承するofstream
の継承ツリーにひし形があるためです。)
最も派生したクラスコンストラクターがその(継承された)仮想ベースコンストラクターを直接呼び出すことはほとんど知られていないか、または忘れられやすい事実です。base-or-member-init-listで明示的に呼び出さない場合、仮想ベースのdefaultコンストラクタが呼び出されます。ただし(これはさらにあいまいです)、暗黙的に定義またはデフォルトとして宣言されたコピー/移動コンストラクターの場合、選択された仮想基本クラスコンストラクターはnotデフォルトのコンストラクタですが、対応するコピー/移動コンストラクタです。これが削除されているかアクセスできない場合、最も派生したクラスのコピー/移動コンストラクターが削除済みとして定義されます。
次に例を示します(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()
、notB::B(int)
。これが、明示的に許可を与えていないクラスから継承することが悪い考えである理由の1つです。そのコンストラクタが最も派生したクラスコンストラクタとして呼び出された場合、継承元のクラスのコンストラクタによって呼び出されるのと同じ仮想ベースコンストラクタを呼び出さない可能性があります。
Libstdc ++とlibcxxの両方で_basic_ifstream
_のmoveコンストラクターが_basic_ios
_のデフォルト以外のコンストラクターを呼び出さないので、あなたができることについては、手書きのmoveコンストラクターが機能すると思います_basic_streambuf
_ポインターからの1つですが、代わりにコンストラクター本体で初期化します(これは [ifstream.cons]/4 が言っているようです)。他の潜在的な問題については、 継承によるC++標準ライブラリの拡張? を読む価値があります。
前の回答で述べたように、コンストラクターは基本クラスで削除済みとして定義されています。つまり、使用できません-<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))
{
}
これは質問のコメントの1つ(@ igor-tandetnik)で確認できます。