У меня есть шаблон класса, который я использую, чтобы «обернуть» несколько распространенных типов контейнеров. Цель состоит в том, чтобы заменить
getItems( vector< ItemType > &x );
getItems( set< ItemType > &x );
getItems( my_custom_vector< ItemType > &x );
(...and several others...)
с одной функцией, которая выглядит следующим образом:
getItems( Cont< ItemType > x );
Урезанная реализация (я буду включать только код для векторов) в настоящее время выглядит следующим образом:
template< class T > ContBase {
public:
ContBase( void *container ) : mContainer( container ) {}
virtual void add( const T& item ) = 0;
virtual void clear() = 0;
protected:
void *mContainer;
};
template< class T > ContVector : public ContBase< T > {
public:
ContVector( std::vector< T > &native_container )
: ContBase( (void *) &native_container ) {}
void add( const T& item ) { vec().push_back(item); }
void clear() { vec().clear(); }
protected:
std::vector< T > &vec() { return *(std::vector< T > *) mContainer; }
};
template< class T > class Cont {
public:
Cont( std::vector< T > &x ) { new (&mMem) ContVector< T >( x ); }
void clear() { container()->clear(); }
void add( const T& item ) { container()->add( item ); }
protected:
ContBase< T > *container() { return (ContBase< T > *) &mMem[ 0 ]; }
unsigned char mMem[ sizeof(ContBase< T >) ];
};
Я провел некоторое начальное тестирование, и оно отлично работало в настройке контролируемых тестов. Я думаю, что здесь главное - хитрое умное использование этого размещения, нового в конструкторе для Cont. Однако, как только я вставил его в реальное приложение (компилятор Visual Studio 2010 C ++) и запустил оптимизацию всей программы (с генерацией кода во время компоновки, оптимизацией по скорости и т. Д.), Я начал видеть серьезные проблемы. В частности, у меня есть такая функция:
void MyClass::myFunc( Index x, Cont< Index > container, uint r )
{
// ... Return if x is invalid
cerr << "VFPTR: " << (void*) ((uint *) ((void*) &verts))[0] << endl;
container.add( x );
// ...Potentially add some more things to container.
}
Который дает мне вывод, подобный этому (обратите внимание, что безумное преобразование типов - это хак, чтобы получить значение таблицы виртуальных функций в VC ++):
VFPTR: 0AD98708
// Repeat above line 17 times
VFPTR: 0018B830
Во-первых, очень странно, что указатель виртуальной функции может отличаться. Во-вторых, раздражает, что этот же код работает в режиме отладки; это отображается только при включенной оптимизации. В-третьих, следует отметить, что новый указатель (0018B830) является указателем на таблицу виртуальных функций в классе base , ContBase. Затем происходит сбой при вызове container.add (x) со значением NULL.
Итак, мой основной вопрос здесь: это юридический код, или я пропустил случай неопределенного поведения? Здесь не работает компилятор Microsoft или мой код не определен для начала?