2011-09-07 3 views
3

У меня есть два класса: класс шаблона, а также регулярный класс, который наследует от него:«Retroactive Union» - это можно сделать?

template <int N> class Vector 
{ 
    float data[N]; 
    //etc. (math, mostly) 
}; 

class Vector3 : public Vector<3> 
{ 
    //Vector3-specific stuff, like the cross product 
}; 

Теперь, я хотел бы иметь переменные-члены х/у/г в дочернем классе (полный члены, а не только геттеры - я тоже хочу их установить). Но чтобы убедиться, что все (унаследованные) математика выработаны, x придется ссылаться на ту же память, что и data[0], y на и т. Д. По сути, я хочу объединение, но я не могу объявить его в базовом классе потому что я не знаю количество поплавков в векторе в этой точке.

Так что - это можно сделать? Есть ли какая-то препроцессор/typedef/template магия, которая достигнет того, что я ищу?

PS: Я использую g ++ 4.6.0 с -std = C++ 0x, если это помогает.

Edit: Хотя ссылки дали бы синтаксис я ищу, идеальное решение не имело бы класса немного больше (и делают ссылки - много Vector<3> 12 байт, Vector3 со ссылками есть. 40!).

+0

Почему вы хотите это, вместо того, чтобы иметь только геттеры и сеттеры, которые ссылаются на соответствующие значения данных? –

+0

Почему геттеры не решают вашу проблему? –

+0

Не будут ли ссылки работать? – Hasturkun

ответ

0

Чтобы закончить старый вопрос: No. Это делает меня грустным, но вы не можете это сделать.

Вы можете подобраться. Такие вещи, как:

Vector3.x() = 42; 

или

Vector3.x(42); 

или

Vector3.n.x = 42; 

или даже

Vector3.x = 42; //At the expense of almost quadrupling the size of Vector3! 

находятся в пределах досягаемости (см другие ответы - все они очень хорошо). Но мой идеал

Vector3.x = 42; //In only 12 bytes... 

просто не выполнимо. Нет, если вы хотите наследовать все свои функции из базового класса.

В конце концов, этот код в конечном итоге получил тонкую настройку - теперь это строго 4-членные векторы (x, y, z, w), использует SSE для векторной математики и имеет несколько классов геометрии (Point , Vector, Scale и т. Д.), Поэтому наследование функций ядра больше не является вариантом для корректности типа. Такие дела.

Надеюсь, это сэкономит кому-то еще несколько дней расстроенного поиска!

8

Как насчет:

class Vector3 : public Vector<3> 
{ 
public: 
    // initialize the references... 
    Vector3() : x(data[0]), y(data[1]), z(data[2]){} 
private: 
    float& x; 
    float& y; 
    float& z; 
}; 

Конечно, если вы хотите, чтобы занять же пространство, то это совсем другая история ...

С небольшой шаблон магии, вы можете сделать следующее ...

#include <iostream> 

template <int N, typename UnionType = void*> struct Vector 
{ 
    union 
    { 
     float data[N]; 
     UnionType field; 
    }; 

    void set(int i, float f) 
    { 
     data[i] = f; 
    } 

    // in here, now work with data 
    void print() 
    { 
     for(int i = 0; i < N; ++i) 
     std::cout << i << ":" << data[i] << std::endl; 
    } 
}; 

// Define a structure of three floats 
struct Float3 
{ 
    float x; 
    float y; 
    float z; 
}; 

struct Vector3 : public Vector<3, Float3> 
{ 
}; 

int main(void) 
{ 
    Vector<2> v1; 
    v1.set(0, 0.1); 
    v1.set(1, 0.2); 
    v1.print(); 

    Vector3 v2; 
    v2.field.x = 0.2; 
    v2.field.y = 0.3; 
    v2.field.z = 0.4; 
    v2.print(); 

} 

EDIT: Прочитав комментарий, я понимаю, что я писал до того не было на самом деле не отличается, так что небольшой твик к пред итерации, чтобы обеспечить прямой доступ к полю (что, как я полагаю, вы после этого), я полагаю, что разница между этим и решением Роба заключается в том, что вам не нужны все специализации для повторной реализации всей логики. .

+0

Конечно, это решение означает, что мы получаем от 'sizeof (float) * 3' (вероятно, 6 байтов) до' sizeof (float * 3) + padding + sizeof (float *) * 3' (вероятно, 32 байта), таким образом 5-кратное увеличение размера объекта. –

+0

В чем преимущество этого подхода по сравнению с сеттерами/геттерами? –

+1

@ AndyT - нет, что-то никогда ... – Nim

3

Вот одна возможность, списаны с моего ответа на this question:

class Vector3 : public Vector<3> 
{ 
public: 
    float &x, &y, &z; 

    Vector3() : x(data[0]), y(data[1]), z(data[2]) { } 
}; 

Это имеет некоторые проблемы, как требуя, чтобы определить свой собственный конструктор копирования, оператор присваивания и т.д.

+0

+1 для обозначения необходимых пользовательских операторов копирования/присваивания. –

7

Как насчет шаблон специализации ?

template <int N> class Vector 
{ 
    public: 
    float data[N]; 
}; 

template <> 
class Vector<1> 
{ 
    public: 
    union { 
    float data[1]; 
    struct { 
     float x; 
    }; 
    }; 
}; 

template <> 
class Vector<2> 
{ 
    public: 
    union { 
    float data[2]; 
    struct { 
     float x, y; 
    }; 
    }; 
}; 


template <> 
class Vector<3> 
{ 
    public: 
    union { 
    float data[3]; 
    struct { 
     float x, y, z; 
    }; 
    }; 
}; 

class Vector3 : public Vector<3> 
{ 
}; 

int main() { 
    Vector3 v3; 
    v3.x; 
    v3.data[1]; 
}; 


EDIT Хорошо, здесь другой подход, но он вводит дополнительный идентификатор.

template <int N> class Data 
{ 
    public: 
    float data[N]; 
}; 

template <> class Data<3> 
{ 
    public: 
    union { 
    float data[3]; 
    struct { 
     float x, y, z; 
    }; 
    }; 
}; 

template <int N> class Vector 
{ 
    public: 
    Data<N> data; 
    float sum() { } 
    float average() {} 
    float mean() {} 
}; 

class Vector3 : public Vector<3> 
{ 
}; 

int main() { 
    Vector3 v3; 
    v3.data.x = 0; // Note the extra "data". 
    v3.data.y = v3.data.data[0]; 
}; 
+0

ваше новое решение выглядит красиво, однако вы уверены, что это четко определено? Я не уверен в стандарте здесь, я знаю, что существуют определенные положения для структуры, разделяющей одну и ту же ведущую последовательность, но я ничего не знаю о наложении структур и массивов. –

+0

Хмм - интригующий! Я вернусь к вам, как только я проверил это ... –

+0

Ха-ха - ДА! Отлично. Позволяет получить доступ к «названным» членам. Не увеличивает размер класса. Именно то, что я искал. Это какая-то глубокая, темная магия шаблона, мой друг! –

1

Вы можете сделать следующее:

template <int N> struct Vector 
{ 
    float data[N]; 
    //etc. (math, mostly) 
}; 
struct Vector3_n : Vector<3> 
{ 
    //Vector3-specific stuff, like the cross product 
}; 
struct Vector3_a 
{ 
    float x, y, z; 
}; 
union Vector3 
{ 
    Vector3_n n; 
    Vector3_a a; 
}; 

Сейчас:

Vector3 v; 
v.n.CrossWhatEver(); 
std::cout << v.a.x << v.a.y << v.a.z 

Вы можете попробовать анонимный накидной трюк, но это не является стандартным и не очень компактен.

Но обратите внимание, что при таком объединении слишком легко попасть в неопределенное поведение, даже не заметив. Тем не менее, это, вероятно, будет работать в любом случае.

1

Я написал способ назад (который также разрешил геттеры/сеттеры), но это был такой не переносимый хариш-хак, который ВАМ ДЕЙСТВИТЕЛЬНО НЕ ДОЛЖЕН СДЕЛАТЬ ЭТО. Но я думал, что все равно выброшу. В основном, он использует специальный тип с 0 данными для каждого члена. Затем функции-члены этого типа захватывают указатель this, вычисляют положение родителя Vector3, а затем используют члены Vector3 для доступа к данным. Этот хак работает более или менее как ссылка, но не берет никакой дополнительной памяти, не имеет проблем с повторным освещением, и я уверен, что это неопределенное поведение, поэтому оно может вызвать nasal demons.

class Vector3 : public Vector<3> 
{ 
public: 
    struct xwrap { 
     operator float() const; 
     float& operator=(float b); 
     float& operator=(const xwrap) {} 
    }x; 
    struct ywrap { 
     operator float() const; 
     float& operator=(float b); 
     float& operator=(const ywrap) {} 
    }y; 
    struct zwrap { 
     operator float() const; 
     float& operator=(float b); 
     float& operator=(const zwrap) {} 
    }z; 
    //Vector3-specific stuff, like the cross product 
}; 
#define parent(member) \ 
(*reinterpret_cast<Vector3*>(size_t(this)-offsetof(Vector3,member))) 

Vector3::xwrap::operator float() const { 
    return parent(x)[0]; 
} 
float& Vector3::xwrap::operator=(float b) { 
    return parent(x)[0] = b; 
} 
Vector3::ywrap::operator float() const { 
    return parent(y)[1]; 
} 
float& Vector3::ywrap::operator=(float b) { 
    return parent(y)[1] = b; 
} 
Vector3::zwrap::operator float() const { 
    return parent(z)[2]; 
} 
float& Vector3::zwrap::operator=(float b) { 
    return parent(z)[2] = b; 
} 
Смежные вопросы