Attention! Bogue dangereux dans l'implémentation C ++ std :: map :: merge et std :: set :: merge dans Visual Studio 2017

Si vous utilisez la norme C ++ 17 dans MS Visual Studio 2017 - soyez prudent: la version actuelle contient un bogue critique dans l'implémentation de std :: map :: merge et std :: set :: merge. Détails - sous la coupe.

Comment se manifeste un bug?


  1. La complexité de std :: map :: merge et std :: set :: merge au lieu du journal N * standard (size () + N)), où N est la taille de la partie ajoutée, s'avère être environ N au carré.
  2. Si un conteneur avec un nombre suffisamment important d'éléments a été ajouté à l'aide de la fusion, lors de la destruction du conteneur résultant, nous obtenons un débordement de pile.
  3. Le conteneur atteint un état incorrect après l'exécution de la fusion, de sorte que d'autres manifestations sont possibles.

Que dit Microsoft?


Le rapport de bogue a été envoyé par moi à Microsoft il y a presque 2 mois.
Dans Visual Studio 2019 Update 2 Preview 2 aurait dû être corrigé.

Mais dans la version actuelle de Visual Studio 2017, le 15.9.12 n'a pas été corrigé jusqu'à présent, et à en juger par les derniers rapports, attendez longtemps ...

Le bogue est le marquage de couleur incorrect des nœuds ajoutés dans l'implémentation de red-ebony.

Comment reproduire?


//#include "pch.h" #include <chrono> #include <string> #include <iostream> #include <map> #include <set> const size_t mainSize = 50'000; const size_t additionSize = mainSize; auto getMainMap() { std::map<size_t, std::string> result; while( result.size() < mainSize ) result.emplace(result.size(), "SomeText"); return result; } auto getAdditionMap() { std::map<size_t, std::string> result; while ( result.size() < additionSize ) result.emplace(mainSize + result.size(), "SomeAdditionText"); return result; } int main() { { auto mainMap = getMainMap(); auto addition = getAdditionMap(); auto start = std::chrono::steady_clock::now(); for ( auto const &elem : addition ) mainMap.emplace(elem); auto end = std::chrono::steady_clock::now(); std::cout << "manual insertion with copy: " << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << std::endl; } { auto mainMap = getMainMap(); auto addition = getAdditionMap(); auto start = std::chrono::steady_clock::now(); mainMap.merge(addition); auto end = std::chrono::steady_clock::now(); std::cout << "std::map::merge: " << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << std::endl; } // <---- and stack overflow here because of incorrect mainMap after merge return 0; } 

En variant la valeur de mainSize, vous pouvez obtenir des résultats différents - soit seulement une exécution lente, soit également un plantage.

Que faire


Révisez votre code et remplacez les appels de fusion par une insertion manuelle. Ou passez à VS 2019.
Et si le code compilé est déjà allé au client ... Ohhh ...

Source: https://habr.com/ru/post/fr455632/


All Articles