Poniższy problem został poddany destylacji z ogromnego projektu i najbardziej minimalnego przykładu problemu, jaki udało mi się wymyślić.Szukam wyjaśnienia błędu korupcji stosu
Wiem, że wywodzenie się z std::string
jest złe i zostało już zmienione w naszej bazie kodu, ale próbuję zrozumieć, co dzieje się pod maską tutaj.
Kod awarii na Visual C++ 2017
Microsoft Visual Studio Community 2017
Version 15.2 (26430.14) Release
Visual C++ 2017 00369-60000-00001-AA257
w trybie tylko uwalniania (z optymalizacją prędkości). Nie ulega awarii w trybie zwolnienia bez optymalizacji prędkości.
#include <string>
#include <string_view>
#include <vector>
struct my_string : public std::string
{
__declspec(noinline)
my_string::my_string(const std::string_view& str) :
std::string(str.data(), str.size())
{}
template <typename T>
my_string& arg(T)
{
return *this;
}
};
struct my_string_view : public std::string_view
{
my_string_view(const std::string_view::value_type* val) :
std::string_view(val) {}
template <typename... PARAMS>
my_string arg(PARAMS&&... prms) {
return my_string(*this).arg(std::forward<PARAMS>(prms)...);
}
};
template <typename T>
struct basic_color
{
T r, g, b, a;
basic_color() : r(0), g(0), b(0), a(255) {}
template <typename U>
explicit basic_color(const basic_color<U>& c) :
r(c.r), g(c.g), b(c.b), a(c.a)
{}
};
using color = basic_color<std::uint8_t>;
using float_color = basic_color<float>;
__declspec(noinline)
void change_float_color(float_color& color)
{
color.r = 0.1f;
}
int main()
{
std::vector<float_color> colors = { {} };
float sum = 0;
for (std::uint32_t i = 0; i < 1; ++i)
{
float_color fc;
change_float_color(fc);
color c(fc);
std::vector<std::string> msgs;
msgs.push_back(my_string_view("").arg(c.r));
msgs.push_back(my_string_view("").arg(c.g));
sum += fc.b - colors[i].b;
}
return static_cast<int>(sqrt(sum));
}
Błąd w Visual Studio jest to (spojrzeć na połamanych rozmiarach msgs
i colors
na dole):
Domyślam się, że połączenie z std::vector<std::string>::push_back(std::string&&)
z my_string
jest problematyczne (zachowanie przypominające cięcie). Ale w jaki sposób może to zepsuć stos (lub wskaźnik stosu)?
Czy ktoś ma pojęcie, co może się tutaj wydarzyć lub jak się dowiedzieć?
Here to mój projekt na wypadek, gdyby ktoś chciał odtworzyć problem.
Użyj debuggera, punktem przełomowym jest tutaj broń. –
"ponieważ std :: string nie ma wirtualnego destruktora, wartości r podane do push_back są pocięte na plasterki." Cięcie nie ma nic wspólnego z wirtualnym destruktorem. Wartości zostaną podzielone niezależnie. "W ten sposób destruktor klasy pochodnej (my_string) nigdy nie jest wywoływany, część śmieci pozostaje na stosie" To nie ma żadnego sensu. 'sizeof (my_string) == sizeof (std :: string)' Nie robi to żadnej różnicy. "spójrz na zepsute rozmiary" Kompilator może swobodnie robić, co chce, z tymi obiektami, ponieważ nigdy nie zostaną użyte ponownie. –
> Moim pierwszym przypuszczeniem było to, że ponieważ std :: string nie ma wirtualnego destruktora, wartości r podane dla parametru push_back są podzielone na kawałki. Zatem destruktor klasy pochodnej (my_string) nigdy nie jest nazywany
Hedede