私は配列を作成しようとしているが、オブジェクトのデフォルトの初期化を行わないコードを持っています。私は完全に新しい配置に進めたいと思いますが、オブジェクトのデストラクタはemplace関数の中で呼び出されています。次いで配置の新規および完璧な転送
start emplace
0 constructed
0 move constructed
0 destructed
end emplace
start emplace
1 constructed
1 move constructed
1 destructed
end emplace
1
start emplace
2 constructed
2 move constructed
2 destructed
end emplace
0 destructed
1 destructed
2 destructed
それは、オブジェクトが一度据え付ける関数内、(明らかUBである)一度構築し、二回破壊されていることを示しており、以下のよう
#include <iostream>
#include <memory> // std::uninitialized_copy, std::allocator...
#include <utility> // std::move...
#include <bitset>
struct Int {
int i;
Int () : i (-1) { std::cout << "default constructed\n"; }
Int (const int i_) : i (i_) { std::cout << i << " constructed\n"; }
Int (Int && int_) : i (std::move (int_.i)) { std::cout << i << " move constructed\n"; }
Int (const Int & int_) : i (int_.i) { std::cout << i << " copy constructed\n"; }
~Int () { std::cout << i << " destructed\n"; i = -1; }
};
template <typename T, size_t S = 64>
class NoInitArray {
std::bitset<S> m_used;
T *m_array = reinterpret_cast < T* > (::operator new (sizeof (T) * S));
public:
T const &operator [ ] (const size_t idx_) const {
return m_array [ idx_ ];
}
NoInitArray () { }
~NoInitArray () {
for (size_t idx = 0; idx < S; ++idx) {
if (m_used [ idx ]) {
reinterpret_cast< const T* > (m_array + idx)->~T ();
}
}
}
template<typename ...Args>
void emplace (const size_t idx_, Args &&... value_) {
std::cout << "start emplace\n";
m_used [ idx_ ] = 1;
new (m_array + idx_) T (std::forward<T> (value_) ...);
std::cout << "end emplace\n";
}
};
int main () {
NoInitArray<Int> nia;
nia.emplace (0, 0);
nia.emplace (1, 1);
std::cout << nia [ 1 ].i << std::endl;
nia.emplace (2, 2);
return 0;
}
このプログラムを実行した結果でありますNoInitArrayの破壊時に一度。
質問は「私のIntオブジェクトのデストラクターがemplace関数の中で呼び出されたのはなぜですか?
コンパイラ、最新のClang/LLVM on Windhoze。
EDIT1:Int構造体にmoveコンストラクタとコピーコンストラクタを追加しました。これでカウントが一致するようになりました(つまり、2回の構造と2回の破壊)。
EDIT2:配置を変更するnew (m_array + idx_) T (std::forward<T> (value_) ...);
からnew (m_array + idx_) T (value_ ...);
への改行は、移動コンストラクタを必要とせずに余分な構築/破壊を回避します。
EDIT3:将来の読者のためだけです。上記のように、〜NoInitArray()はメモリをリークします。 m_arrayのdeleteを呼び出すことは悪いニュースであり、m_array [0]のデストラクタ(Clang/LLVM)(これまでのところ理解していない限り、つまりUBは保証されていません)です。 std :: malloc/std :: freeは行く方法に見えますが、もしあなたがそうすれば、すべての地獄が失われ、1人は足を失うかもしれません。
コピーコンストラクタコールも必ずカウントしてください。 –
@BenVoigtコピーコンストラクタは通常のコンストラクタを呼び出しませんか?これが問題であれば、これは転送が意図したとおりに機能しないことを意味します。この作業を行うにはどうすればいいですか? – degski
いいえ、コピーコンストラクションはデフォルトのコンストラクタにチェーンされません。コピーではなく移動を期待している場合は、移動コンストラクタとコピーコンストラクタの両方のユーザ定義バージョンを提供し、それぞれに異なるメッセージを記録します。 –