2016-06-16 3 views
0

数日前、私は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つのコンパイラが正しく動作するかどうかです。

+1

FWIWコードは[完全に異なります](https://github.com/boostcon/cppnow_presentations_2012/blob/master/fri /value_semantics/value_semantics.cpp#L193)を入力します。あなたが未定義の振る舞いをしているなら、私は驚くことはありません。 –

+0

スニペットを短くするには、代入演算子を削除し、コンストラクタを移動します。何も違いがないためです。この動作は関係なく継続されます。 –

答えて

1

List-initialization priority from object of same typeと同じです。 clangは正しいです。

[dcl.init.list]/3:

[...] Tは、クラス型であり、初期化リストはUをT型CV U、単一の要素を有する場合またはTから派生したクラスである場合、オブジェクトはその要素から初期化されます。[...]

+0

私はそれが同じではないと思います。 'std :: move'の使用は明らかに無効ですか? OTOH、その優先順位は今CLangのために修正されているようだ... – Elijan9

+0

@ Elijan9はあなたの質問に答えました。 – ecatmur

+0

あなたが正しいです、彼はコンストラクタ - 引数 - 値と - 移動のイディオムを正しく使用しています。残念なことに、このイディオムは正しく動作しません。なぜならリストの初期化の優先順位はGCCにとって間違っているからです(そしてこれはCLangのために使われていました)。しかし、コンストラクタ - 引数によるユニバーサル参照と転送を使用すると、gccやCLangの古いバージョンでも正しく動作しますか? – Elijan9

関連する問題