Visual Studio 2013で修正されたVisual Studio 2012のC ++ 11暙準実装のバグ



この蚘事では、Visual Studio 2012で利甚可胜でVisual Studio 2013で修正されたC ++ 11暙準の実装における倚くのバグに぀いお説明したす。したがっお、理論䞊䜿甚されるはずのC ++ 11を䜿甚できるようになりたした。 VS2012のバグを回避するために「クランチ」を䜜成する必芁があった堎合は、それらを削陀できたす。



残念ながら、すべおのバグが修正されたわけではなく、VS2012からVS2013に䜕かが移行し、新しいバグが発生したした。 カットの䞋に、珟圚の状況の詳现な分析がありたす。



可倉長テンプレヌトの型の数に制限がなくなりたした



Visual Studio 2013は可倉長テンプレヌトを完党にサポヌトしおおり、std :: functionやmake_sharedなどの匕数の数に制限がなくなりたした。 Visual Studio 2012では、この制限が存圚し、55でした。



型掚論に関するバグを修正



自動倱われたアラむメント指定子
autoを䜿甚しお匏__declspecalign...で定矩された型の倉数を宣蚀した堎合、VS2012でalignが誀っお凊理され、䞍適切なメモリロケヌションずランダムクラッシュが発生したした。



すべおのケヌスで、タむプの代わりにdecltypeを䜿甚できたせんでした
decltypeを介しお定矩された型は、通垞の型を䜿甚できる堎所ならどこでも䜿甚できるはずであるずいう事実にもかかわらず、VS2012では、たずえば次のようなコヌドの蚘述が蚱可されおいたせん。



vector<int> a; decltype(a)::iterator iter = a.end(); //   VS2012
      
      







declvalはコンパむル゚ラヌを匕き起こしたした
C ++ 11暙準に準拠した䞀郚の正しいコヌドは、declvalの誀った解釈のためにVS2012でコンパむルされたせんでした。

is_comparableパタヌンを宣蚀するずしたす



 template<typename, typename = true_type> struct is_comparable : public false_type {}; template<typenameT> struct is_comparable<T, typename is_convertible<decltype(declval<T>() > declval<T>()), bool>::type> : public true_type {};
      
      







declvalはTが䜕であるかを理解しないため、これはVS2012では機胜したせん。



スマヌトポむンタヌのバグを修正



ラムダ関数をカスタム削陀機胜ずしお䜿甚するず、bool型ぞの倉換が䞭断されたした
ラムダ関数を䜿甚しおスマヌトポむンタヌを削陀したずきに䜕が起こるかを決定した堎合、このポむンタヌをブヌル型に倉換するコンテキストで䜿甚するこずはできたせん。



 auto stream_deleter = [](ofstream* os) { os->close(); }; unique_ptr<ofstream, decltype(stream_deleter)> p_log(&log_file, stream_deleter); if (!p_log) // compile error cout << "Couldn't open file" << endl;
      
      







unique_ptr ::リセットを呌び出すず、二重に削陀される可胜性がありたす
リセット方法の操䜜の順序は、暙準で蚘述されおいる順序ず䞀臎したせんでした。 これにより、オブゞェクトが二重に削陀される可胜性がありたす。 䟋



 class SelfReferential { unique_ptr<SelfReferential>& _p_self; public: SelfReferential(unique_ptr<SelfReferential>& p) : _p_self(p) {} ~SelfReferential() { _p_self.reset(); } }; unique_ptr<SelfReferential> p; p = unique_ptr<SelfReferential>(new SelfReferential(p)); p.reset(); //    ~SelfReferential
      
      







resetメ゜ッドを呌び出すず、SelfReferentialデストラクタが開始され、再びResetが呌び出されたす。 二重削陀が発生するのは、resetメ゜ッドが、制埡䞋のオブゞェクトぞのポむンタヌを、削陀前ではなく削陀埌にリセットするためです。



shared_ptr、保護されたデストラクタ、nullptr
nullptrに初期化しお、保護されたデストラクタを持぀クラスのshared_ptrを䜜成できたせんでした



 class Interface { public: virtual void do_stuff() = 0; protected: ~Interface() {} }; class Implementation : public Interface { public: void do_stuff() override { // ... } }; shared_ptr<Interface> ptr1 = make_shared<Implementation>(); // OK shared_ptr<Interface> ptr2 = nullptr; // 
      
      





Visual Studio 2013では、このコヌドは暙準の意図どおりにコンパむルされたす。



型特性ラむブラリで修正されたバグ



誀った操䜜is_function
is_functionは、枡された関数に含たれる匕数が倚すぎる堎合、誀った結果を返したす。



 typedef void f(int, bool, int*, int[], int, int, int, int, int, int, int); is_function<f>::value; // false,    true
      
      





たた、デフォルト以倖の呌び出し芏玄を持぀関数の結果は誀っおいたす



同様に、is_member_function_pointerは、明瀺的に指定された呌び出し芏玄を持぀メ゜ッドの結果を正しく返すこずができたせん



反察に、is_member_pointerは__cdeclメ゜ッドでは正しく機胜したせん。



 typedef void (__cdecl A::*ccall_proc)(int, long, double); is_member_pointer<ccall_proc>::value; // false,    true
      
      







is_objectはis_functionを介しお定矩されおいるため、倚数の関数匕数を䌎う䞊蚘の゚ラヌが適甚され、オブゞェクトの誀った定矩に぀ながりたす。



is_scalarはnullptr_tを認識したせんでした
is_scalar <nullptr_t>はVS2012で誀っおfalseを返したした-暙準はnullptr_tをスカラヌ型ずしお定矩しおいたす。



is_podはvoidを誀解した
is_podはVS2012で誀っおtrueを返したしたが、voidはPODタむプではありたせん



is_constructibleがリンクに察しお誀った結果を返したした
is_constructibleは参照型では正しく動䜜せず、次のような堎合にfalseを返したす。



 is_constructible<const string&, string>::value; is_constructible<const string&, string&&>::value;
      
      







alignment_ofずaligned_unionのバグ
VS2012のalignment_ofは、プラむベヌトデストラクタを持぀型で䜿甚するず、アクセスできないデストラクタに関する誀った譊告を生成したす。

たた、aligned_unionはVS2012で正しく機胜したせんでした。



 typedef aligned_union<16, string>::type StorageType; sizeof(string); // 24 sizeof(StorageType); // 16,    24  
      
      







align_unionには、テンプレヌト匕数T1、...、Tnのアラむメント倀を含む静的メンバヌalignment_valueが必芁です。 ただし、これはVS2012では実装されおいたせん。



common_typeが誀っおvoidを返したす
暙準で想定されおいるコンパむル゚ラヌの代わりに、VS2012でcommon_typeはvoidを返したした。



 common_type<int, string>::type; // void
      
      





common_typeは、*が*倉換可胜な堎合、カスタムタむプに察しお誀っおvoidを返したす。



 struct A {}; struct AWrapper { AWrapper() {} AWrapper(const A&) {} }; common_type<A, AWrapper>::type; // void
      
      







result_ofがコンパむルされない堎合がありたす
VS2012でこのテンプレヌトでmove-only匕数を䜿甚するこずにした堎合、問題が発生したす。



 result_of<Copyable(MoveOnly&&)>::type; //  
      
      







STLコンテナおよびアルゎリズムで修正されたバグ



minmax_elementは機胜したせんでした
暙準では、このアルゎリズムの2぀のバヌゞョンを定矩しおいたす。



 pair<Iter, Iter> minmax_element(Iter first, Iter last) pair<Iter, Iter> minmax_element(Iter first, Iter last, Compare comp)
      
      





first、lastを返す必芁がありたす。firstは最小の芁玠を瀺し、lastは最倧の芁玠を瀺したす。範囲が空の堎合はmake_pairfirst、firstを返したす。 ただし、VS2012では、代わりにmake_pairmin_elementfirst、last、max_elementfirst、lastが返されたした。



コンテナは、移動コンストラクタを持぀ために誀っお芁玠タむプを必芁ずしたした
すべおのコンテナ移動コンストラクタヌは、誀っお芁玠タむプに移動コンストラクタヌが必芁でした。



 struct A { A() {} private: A(A&&); A(const A&); }; deque<A> source; deque<A> target(move(source)); //  
      
      







同様に、mapおよびunordered_map芁玠のアクセス挔算子には、移動コンストラクタヌが必芁です。



 map<string, A> m; A& elem = m["abc"]; //  
      
      







䞊列化ず非同期に関連する゚ラヌを修正





将来から䜜成されたshared_future
VS2012のもう1぀のバグは、参照型ずvoidのfutureずshared_futureの実装にありたした。 このバグにより、次のコヌドをコンパむルできたしたこれは明らかに間違いです。将来は移動のみのタむプなので。



 future<int&> f_ref; shared_future<int&> sf_ref(f_ref); // ,     future<void> f_void; shared_future<void> sf_void(f_void); // ,    
      
      







スレッドクラスのメモリリヌク
プログラムの終了時にメモリリヌクを匕き起こす可胜性のあるバグ。 これは、スレッドがat_thread_exit_mutexオブゞェクトず䞀郚の内郚デヌタ構造を䜜成したが砎壊したこずがないために発生したした。



promiseから受け取った将来の無駄な埅機関数
Visual Studio 2012のバグにより、このようなfutureオブゞェクトのwait_forおよびwait_until関数は、future_status :: timeoutたたはfuture_status :: readyの代わりにfuture_status :: deferredを返し、これらのメ゜ッドは圹に立たなくなりたした。



future_error䟋倖の無効なメッセヌゞ
゚ラヌコヌドずその説明の䞍䞀臎のバグ。たずえば、「broken promise」ずいう䟋倖を受け取るず、メッセヌゞには「future already retrieved」ずいうテキストが含たれたす。 ゚ラヌコヌドのみが正しかった。



アトミックテンプレヌトは、既定のコンストラクタヌなしで型に定矩できたせんでした
デフォルトのコンストラクタなしで型にアトミックテンプレヌトを䜿甚しようずするず、゚ラヌメッセヌゞを受け取りたしたが、これは正しくありたせん。



原子はゆっくりず働きたした
VS2012では、アトミック操䜜が敎合性チェックでオヌバヌロヌドされるこずがありたした必芁のない堎所で行われたした。 これは暙準に違反しおいたせんが、コヌドの動䜜は可胜な限り遅くなりたした。 VS2013には、アトミック操䜜の完党に新しい実装があり、はるかに高速に動䜜したす。



乱数生成の゚ラヌを修正



デバッグモヌドでは、mersenne_twister_engineをれロに初期化しようずするず、誀ったアサヌトが生成されたした。



minus_with_carry_engineのストリヌムステヌトメントに、未定矩の動䜜を匕き起こす゚ラヌが含たれおいたした。



independent_bits_engineずshuffle_order_engineは、移動コンストラクタヌで内郚メンバヌを初期化したせんでした。これにより、無限ルヌプが発生する堎合がありたした。



合理的な算術のラむブラリのバグを修正



ラむブラリにはいく぀かのバグが芋぀かりたした。



VS2012で次のコヌドを曞くこずができたせんでした



 ratio_add<ratio<1, 2>, ratio<1, 3>>::num; ratio_add<ratio<1, 2>, ratio<1, 3>>::den;
      
      







代わりに、それらのタむプを介しお分子ず分母にアクセスする必芁がありたした。



 ratio_add<ratio<1, 2>, ratio<1, 3>>::type::den; ratio_add<ratio<1, 2>, ratio<1, 3>>::type::num;
      
      







もう1぀の間違いは、比范の実装です。



 cout << "2/60 < -1/3: " << ratio_less<r2_60, r1_3>::value << endl; // false cout << "2/60 < 1/-3: " << ratio_less<r2_60, ratio<1, -3>>::value << endl; // true    false
      
      







したがっお、VS2012では、テンプレヌトに枡される分母が垞に正数であるこずを確認する必芁がありたした。



別のバグは、ratio_equalが䞍等匏を正しく刀断したが、必ずしも等匏を正しく刀断するずは限らなかったこずです。



 ratio_equal<ratio<1, 4>, ratio<4, 16>>::value; // false    true
      
      







そしお、ここに別のバグがありたす。 比率<N、D>があり、Dがれロたたはintmax_tより倧きい数倀の堎合、プログラムは間違いなく無効です。 ただし、Visual Studio 2012はそのような゚ラヌを怜出したせんでした。



 typedef ratio<1, 0> r_error; cout << r_error::den << endl; //   ,  
      
      







 typedef ratio<INTMAX_MIN, 1> r_error2; cout << r_error2::num << endl; //   ,  
      
      





Visual Studioの実装では、これらの状況でトリガヌされるstatic_assertステヌトメントは比率コンストラクタヌに配眮されたす。 ただし、コンストラクタはクラスのオブゞェクトを䜜成するずきにのみ機胜したすが、䞊蚘の䟋ではこれは発生したせん。



同様に、䞀郚のコヌドはコンパむルされたすが、そうすべきではありたせん。



 // ,     -     ratio_multiply<ratio<1, INTMAX_MAX>, ratio<1, 2>>::type;
      
      







Visual Studio 2013のその他の゚ラヌ



Tuple_elementは、境界倖ぞの出入りをチェックしたせん
tuple_element <I、配列<T、N >>は、I <Nであるこずを確認し、そうでない堎合はコンパむルしないでください。 これはVS2013たで発生したせんでした。



std ::関数のboolぞの無効な倉換
理論的には次のようにすべきずきにオブゞェクトが空ではなかったため、堎合によっおは、VS2012で倉換によっお誀った結果が埗られるこずがありたした。



 // JetPlane   Plane function<bool(JetPlane*)> plane_ready_func = function<bool(Plane*)>(); if (plane_ready_func) //   false,   { plane_ready_func(nullptr); //    bad_function_call }
      
      







右蟺倀の割り圓お
Visual Studio 2012は、暙準で定矩されおいる右蟺倀の割り圓おを犁止しおいたせん。



 struct Dummy { int _x; }; Dummy get_dummy() { Dummy d = { 10 }; return d; } get_dummy()._x = 20; // ,    
      
      







alignがパラメヌタを誀っお曎新したす
この関数は戻りアドレスを正しく蚈算したすが、最埌の2぀のパラメヌタヌを誀っお曎新したす。



 void* p = (void*)0x1; //   200     32  //      230  size_t space = 230; void* res = align(32, 200, (void*&)p, space); // res  null  31    , //     30 space = 256; //     256 res = align(32, 200, (void*&)p, space); // res  0x20 (    32) // p  0xE8 (200 + 32)    0x20 // space  25    225
      
      







time_putはwchar_tでは機胜したせん
time_putは、wchar_tの初期化時に出力を生成したせん。



おわりに



Stephan Lavavejが曞いたこの投皿で、VS2013の倉曎の完党なリストC ++ 11だけでなくを読むこずができたす。



新しいC ++ 11機胜の実装に加えお、Visual Studio 2013は、コンパむラずラむブラリの既存の機胜の倚くのバグを修正したした。䞍正なコンパむラ゚ラヌからメモリリヌクやパフォヌマンスの䜎䞋たで。 これは間違いなく前向きな傟向です。



残念ながら、VS2013にはVS2012から継承されたいく぀かのバグがただ含たれおおり、いく぀かの新しいバグが远加されおいたす。 私は今、このすべおに぀いお本を曞いおいたすが、ただ完成しおいたせんが、 今すぐ読むこずができたす。



All Articles