Множественное наследование и указатель this

Предположим, у меня есть эта структура:

struct vector_data
{
    double x, y;

    double& operator[](size_t index)
    {
        return * (static_cast<double*>(static_cast<void*>(this)) + index);
    }
};

Оператор[] должен работать должным образом, потому что vector_data — это тип POD. Ожидаемое поведение заключается в том, что vector_data[0] возвращает x, а vector_data[1] возвращает y.

Теперь предположим, что у меня есть вторая структура:

struct more_data
{
    double evil_data;

    // There could be more here, data or functions
};

И получить от обоих вот так:

struct composed : public more_data, public vector_data
{
};

Будет ли это разрушать ожидаемое поведение оператора []? Другими словами, будет ли this-указатель vector_data в производной структуре по-прежнему указывать на часть vector_data структуры или он будет указывать на начало производной структуры?

Если он уничтожает operator[], то как я могу решить эту проблему? Я могу сначала наследовать от vector_data, но предположим, что состав содержит виртуальные функции. Я знаю, что большинство компиляторов помещают vtable в конец, но это не гарантируется. Что было бы лучшим подходом?


person Ruud    schedule 25.12.2011    source источник
comment
Вы выполняете арифметику указателя на void*, что должно работать именно так, как ожидалось (или, скорее, что вы подразумеваете под ожидаемым)?   -  person Mat    schedule 25.12.2011
comment
См. редактирование. Это всего лишь пример более общего вопроса: на что указывает указатель this?   -  person Ruud    schedule 25.12.2011
comment
Я так понимаю это просто и пример, но арифметика по void* указателям не определена, внутреннее приведение бесполезно.   -  person Mat    schedule 25.12.2011
comment
Иногда компилятор жалуется, что не может привести указатель типа x к типу y, поэтому сначала эта проблема решается приведением к указателю типа void.   -  person Ruud    schedule 25.12.2011
comment
Разве вы не можете просто вернуть x или y на основе значения индекса?   -  person Yaniro    schedule 25.12.2011
comment
Ах, Мэт, теперь я понимаю, что ты имел в виду под неправильной арифметикой. Я забыл ). Яниро: да, могу, но будет ли это так же быстро, как использование смещения?   -  person Ruud    schedule 25.12.2011
comment
static_cast<double*>(static_cast<void*> почему не просто reinterpret_cast?   -  person curiousguy    schedule 26.12.2011


Ответы (2)


Если оставить в стороне проблемы с вашей неправильной арифметикой указателя (возможность заполнения между x и y делает ваше предположение недействительным), вот краткая иллюстрация того, что происходит с указателем this при использовании множественного наследования:

#include <iostream>
using namespace std;

struct a {
    int aa;
    void showA() {
        cerr << this << endl;
    }
};
struct b {
    int bb;
    void showB() {
        cerr << this << endl;
    }
};
struct c : public a, b {
    int cc;
    void showC() {
        cerr << this << endl;
    }
};
int main() {
    c x;
    x.showA();
    x.showB();
    x.showC();
}

showA и showB печатают разные числа; showC печатает то же число, что и showA, потому что a стоит первым в списке баз. Если поменять местами a и b, то showC и showB будут одинаковыми. «Магия» заключается в компиляторе C++: он достаточно умен, чтобы дать каждой функции-члену правильный указатель this.

person Sergey Kalinichenko    schedule 25.12.2011

Вероятно, вы хотите что-то вроде:

struct vector_data
{
   union 
   {
        struct 
        {
            double x, y;
        }; 
        double data[2];
   }; 

   double& operator[](size_t index)
   {
       return data[index];
   }
}
person ronag    schedule 25.12.2011
comment
Это примерно то, чего я хотел добиться, но проблема в том, что он также предоставляет элемент данных. - person Ruud; 25.12.2011
comment
Обратите внимание, что безымянные объединения и структуры, которые переполняют свои члены в окружающий класс, являются расширением MSVC, а не стандартным C++, и поэтому не переносимы. - person Xeo; 26.12.2011