The following problem is distilled from a huge project and the most minimal example of the problem I was able to come up with.
I know, deriving from std::string
is bad, and it already is changed in our code base, but I am trying to understand what is happening under the hood here.
The code crashes on Visual C++ 2017 (ver 15.2) in release-mode only (with speed optimization).
// my_string.h
#pragma once
#include <string>
struct my_string :
public std::string
{
my_string( const std::string_view& str );
~my_string();
template <typename T>
my_string& arg( T );
};
template <typename T>
my_string& my_string::arg( T )
{
return *this;
}
// my_string.cpp
#include "my_string.h"
#include "my_string_view.h"
my_string::my_string( const std::string_view& str ) :
std::string( str.data(), str.size() )
{}
my_string::~my_string()
{}
// my_string_view.h
#pragma once
#include "my_string.h"
#include <string>
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 )... );
}
};
// main.cpp
#include "my_string_view.h"
#include <string>
#include <vector>
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( static_cast<T>(c.r) ),
g( static_cast<T>(c.g) ),
b( static_cast<T>(c.b) ),
a( static_cast<T>(c.a) )
{}
};
using color = basic_color<std::uint8_t>;
using float_color = basic_color<float>;
__declspec(noinline)
bool change_float_color( float_color& color )
{
color.r = 0.1f;
return true;
}
int main()
{
std::vector<float_color> colors = { {} };
double sum = 0.0;
for ( int 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 ));
}
The error in Visual Studio is this (have a look at the broken sizes of msgs
and colors
at the bottom):
My first guess was that since std::string
does not have a virtual destructor, the r-values given to push_back
are sliced. Thus the destructor of the derived class (my_string
) is never called, some trash remains on the stack and the stack-pointer is off by some bytes. But sizeof(my_string) == sizeof(std::string)
so I do not see how this can corrupt the stack.
Does anybody have an idea what could be happening here or how I can find out?
Aucun commentaire:
Enregistrer un commentaire