数日前、私はSean Parentの話「継承は悪の基本クラスです」を見て彼のコードを試してみる。いくつかの変更を行っている間、私はこの奇妙な行動につまずい:メンバーイニシャライザリストのブレース初期化を使用するとstd :: vectorのコピーオーバーフローが発生する(GCCではなくClangで)
#include <vector>
#include <memory>
using namespace std;
class object_t {
public:
template <typename T>
object_t(T x) : self_{make_unique<model<T>>(move(x))} {}
//explicit object_t(T x) : self_{make_unique<model<T>>(move(x))} {} //this "solves" the problem
object_t(const object_t &x) : self_(x.self_->copy_()) {}
private:
struct concept_t {
virtual ~concept_t() = default;
virtual unique_ptr<concept_t> copy_() const = 0;
};
template <typename T> struct model : concept_t {
model(T x) : data_{move(x)} {} //*the behavior is caused by
//this brace-initialization
//model(T x) : data_(move(x)) {} //this works fine
unique_ptr<concept_t> copy_() const override {
return make_unique<model<T>>(*this);
}
T data_;
};
unique_ptr<const concept_t> self_;
};
int main() {
object_t i{5};
object_t v{vector<int>{1, 2, 3, 4}};
object_t ic{i};
object_t vc{v};
vector<object_t> vv;
vector<object_t> vvv1(vv);
vector<object_t> vvv2 = vv;
vector<object_t> vvv3{vv}; //this fails with a stack overflow in GCC 6.1.1 but only if brace-initialization is used in model<T>
}
私はGCC 6.1.1を使用し、クラン3.8.0および3.9.0クラン(非常に最近のビルド)。スタックオーバーフローは、コードがGCCでコンパイルされ、モデルのコンストラクタでブレースの初期化が使用されている場合にのみ発生します。ここで
は、アドレス・消毒剤の出力です:
ASAN:DEADLYSIGNAL
=================================================================
==21125==ERROR: AddressSanitizer: stack-overflow on address 0x7fff8d492ff8 (pc 0x7f41c188eb02 bp 0x000000000020 sp 0x7fff8d493000 T0)
#0 0x7f41c188eb01 in __sanitizer::StackDepotBase<__sanitizer::StackDepotNode, 1, 20>::Put(__sanitizer::StackTrace, bool*) /build/gcc-multilib/src/gcc/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h:96
#1 0x7f41c188e657 in __sanitizer::StackDepotPut(__sanitizer::StackTrace) /build/gcc-multilib/src/gcc/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc:110
#2 0x7f41c17cffee in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) /build/gcc-multilib/src/gcc/libsanitizer/asan/asan_allocator.cc:420
#3 0x7f41c17cffee in __asan::asan_memalign(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType) /build/gcc-multilib/src/gcc/libsanitizer/asan/asan_allocator.cc:703
#4 0x7f41c1871df7 in operator new(unsigned long) /build/gcc-multilib/src/gcc/libsanitizer/asan/asan_new_delete.cc:60
#5 0x406018 in std::_MakeUniq<object_t::model<std::vector<object_t, std::allocator<object_t> > > >::__single_object std::make_unique<object_t::model<std::vector<object_t, std::allocator<object_t> > >, std::vector<object_t, std::allocator<object_t> > >(std::vector<object_t, std::allocator<object_t> >&&) /usr/include/c++/6.1.1/bits/unique_ptr.h:787
#6 0x40356b in object_t::object_t<std::vector<object_t, std::allocator<object_t> > >(std::vector<object_t, std::allocator<object_t> >) /home/theo/test/valuepoly.cpp:16
#7 0x409169 in object_t::model<std::vector<object_t, std::allocator<object_t> > >::model(std::vector<object_t, std::allocator<object_t> >) /home/theo/test/valuepoly.cpp:25
//The same calls repeat again and again...
#251 0x406046 in std::_MakeUniq<object_t::model<std::vector<object_t, std::allocator<object_t> > > >::__single_object std::make_unique<object_t::model<std::vector<object_t, std::allocator<object_t> > >, std::vector<object_t, std::allocator<object_t> > >(std::vector<object_t, std::allocator<object_t> >&&) /usr/include/c++/6.1.1/bits/unique_ptr.h:787
#252 0x40356b in object_t::object_t<std::vector<object_t, std::allocator<object_t> > >(std::vector<object_t, std::allocator<object_t> >) /home/theo/test/valuepoly.cpp:16
SUMMARY: AddressSanitizer: stack-overflow /build/gcc-multilib/src/gcc/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h:96 in __sanitizer::StackDepotBase<__sanitizer::StackDepotNode, 1, 20>::Put(__sanitizer::StackTrace, bool*)
==21125==ABORTING
私は問題が消えるように明示的なobject_tのコンストラクタに注釈を付ける場合。私はbrace-initializerを使用するとgccがすべてをobject_tに暗黙的に変換し、vectorのstd :: initializer_listコンストラクタを使用すると推測しています。これは再帰的に発生します。
私の質問は、2つのコンパイラが正しく動作するかどうかです。
FWIWコードは[完全に異なります](https://github.com/boostcon/cppnow_presentations_2012/blob/master/fri /value_semantics/value_semantics.cpp#L193)を入力します。あなたが未定義の振る舞いをしているなら、私は驚くことはありません。 –
スニペットを短くするには、代入演算子を削除し、コンストラクタを移動します。何も違いがないためです。この動作は関係なく継続されます。 –