Я работаю над многоплатформенным кодом (Windows, Linux и Mac), написанным на C ++. Сегодня мне пришлось найти проблему, которая возникла только в Windows, и похоже, что это проблема компилятора. Класс с несколькими базовыми классами вызывает метод базового класса, но при этом передается неправильное значение.
Я написал короткий пример C ++, чтобы воспроизвести эту проблему:
#include <stdio.h>class base1
{
public:
int x;
base1(int x):x(x)
{
printf("%s(): this=%p x=%d\n", __FUNCTION__, this, x);
}
void printx()
{
printf("%s(): this=%p x=%d\n", __FUNCTION__, this, x);
}
void print()
{
printf("%s(): this=%p\n", __FUNCTION__, this);
printx();
}
};
template <class T>
class base2
{
public:
int y;
base2(int y):y(y)
{
printf("%s(): this=%p y=%d\n", __FUNCTION__, this, y);
}
void printy()
{
printf("%s(): this=%p y=%d\n", __FUNCTION__, this, y);
}
void print()
{
printf("%s(): this=%p\n", __FUNCTION__, this);
printy();
}
};
class derived: public base1, public base2<derived>
{
public:
int z;
derived()
: base1(1)
, base2(2)
, z(3)
{
printf("%s(): this=%p z=%d\n", __FUNCTION__, this, z);
}
void printz()
{
printf("%s(): this=%p z=%d\n", __FUNCTION__, this, z);
}
void print()
{
printx();
base1::printx();
base1::print();
printf("-----\n");
printy();
base2::printy(); // <--- this call does not work with vs2005
base2::print(); // <--- and this call doesn't work, too.
base2<derived>::printy();
base2<derived>::print();
printf("-----\n");
printz();
}
};
int main(int argc, char *argv[])
{
derived d;
printf("-----\n");
d.print();
}
Код компилируется без каких-либо предупреждений в Windows (с использованием vs2005 и vs2010), Linux (g ++ (Gentoo 6.4.0-r1 p1.3) 6.4.0) и MacOS (Apple LLVM версии 8.1.0 (clang-802.0.42)) ,
Код должен всегда печатать x = 1, y = 2 или z = 3.
Но скомпилированный исполняемый файл vs2005 показывает y = 3 для вызовов base2 :: printy () и base2 :: print ().
base1::base1(): this=0018FF3C x=1
base2<class derived>::base2(): this=0018FF40 y=2
derived::derived(): this=0018FF3C z=3
-----
base1::printx(): this=0018FF3C x=1
base1::printx(): this=0018FF3C x=1
base1::print(): this=0018FF3C
base1::printx(): this=0018FF3C x=1
-----
base2<class derived>::printy(): this=0018FF40 y=2
base2<class derived>::printy(): this=0018FF44 y=3 <--------
base2<class derived>::print(): this=0018FF44
base2<class derived>::printy(): this=0018FF44 y=3 <--------
base2<class derived>::printy(): this=0018FF40 y=2
base2<class derived>::print(): this=0018FF40
base2<class derived>::printy(): this=0018FF40 y=2
-----
derived::printz(): this=0018FF3C z=3
Вызов работает, как и ожидалось, когда base2 является обычным классом, а не шаблоном класса.
Поскольку base2 является шаблоном класса, полное имя базового класса — не base2, а base2<полученный>, Тем не менее, ни один компилятор не жалуется на отсутствующий аргумент шаблона, но vs2005 тихо генерирует код, передавая неверный указатель this в вызываемый метод.
Это «просто» ошибка компилятора в vs2005 или это неопределенное поведение, на которое не жалуется ни один компилятор?
Задача ещё не решена.
Других решений пока нет …