2010-07-29 13 views
6

Mam matrycy klasy C++, który udostępnia szereg metod, npdodawania metod do szablonu specjalizacji

template<int X, int Y> 
class MyBuffer { 
public: 
    MyBuffer<X,Y> method1(); 
}; 

Teraz chcę narażać dodatkowe metody do tej klasy, jeśli X == Y. I zostało to zrobione przez podklasy MyBuffer,

template<int X> 
class MyRegularBuffer : public MyBuffer<X,X> { 
public: 
    MyRegularBuffer method2(); 
}; 

Problem polega na tym, że chcę móc np.

MyRegularBuffer<2> buf = ... 
MyRegularBuffer<2> otherBuf = buf.method1().method2(); 

Ale nie jestem pewien, jak to osiągnąć. Próbowałem myśleć o konstruktorach kopiowania, operatorach konwersji itp., Ale moje umiejętności w C++ są niestety trochę zardzewiałe.

EDIT: Dodam, że tworzenie tych obiektów jest stosunkowo tanie (a także, że nie nastąpi dużo), co oznacza, że ​​będzie OK, aby zrobić coś takiego:

MyRegularBuffer<2> buf = ... 
MyRegularBuffer<2> temp = buf.method1(); // Implicit conversion 
MyRegularBuffer<2> otherBuf = temp.method2(); 

The pytanie brzmi, jak mogę zdefiniować taką konwersję. Operator konwersji musi znajdować się w MyBuffer, ale chcę, aby był dostępny tylko, jeśli X == Y.

+1

zupełnie niezrozumiałe. Na przykład mówisz o "operatorze konwersji", ale go nie ma. Opublikuj jakiś prawdziwy kod. –

+0

@Neil, myślę, że użytkownik zadał prawdziwe pytanie najlepiej jak potrafił. I myślę, że rozumiem to nieco. –

+0

@Aaron W takim przypadku prosimy o podzielenie się z nami swoją wiedzą. –

Odpowiedz

5

Nie potrzebujesz osobnej klasy do reprezentowania specjalny zachowanie. Częściowa specjalizacja umożliwia leczenie niektórych przypadków MyBuffer < X, Y > i nadawanie im dodatkowych metod.

Zachować oryginalne deklarację MyBuffer < X, Y > i dodać to:

template<int Y> 
class MyBuffer<Y, Y> { 
public: 
    MyBuffer<Y,Y> method1(); 
    MyBuffer<Y,Y> method2(); 
}; 

MyBuffer<1,2> m12; m12.method2(); // compile fail, as desired, as it doesn't have such a method because 1 != 2 
MyBuffer<2,2> m22; m22.method2(); // compile success 

Edit: moje ostatnie linie nie były bardzo przydatne po wszystkim, jak podkreślił Georg w komentarzach, więc je usunąłem.

+1

Jedynym minusem jest to, że metoda1() musi zostać ponownie zaimplementowana w MyBuffer , inaczej kompilator będzie uskarżał się na nieznaną metodę podczas próby wywołania MyBuffer :: method1(). AFAIK, nie ma sposobu, aby MyBuffer :: method1() delegował swoją implementację do MyBuffer :: method1() bez określania różnych parametrów szablonu, w których X! = Y. –

+0

Wyliczanie z 'MyBuffer' nie działałoby - to doesn nie zna klasy pochodnej i dlatego nie może zwrócić odpowiedniego typu z 'method1()'. –

+0

@ Georg, nie rozumiem. Czy możesz dokładniej określić, która linia zawiedzie? Skompilowałem swój kod i działa. Ale muszę przyznać, że błędnie skopiowałem ostatnią linię o "klasie MyRegularBuffer". Zaktualizuje się teraz. –

1

Możliwe jest robienie tego, co chcesz, jeśli method1 i method2 zwrócą numer referencyjny do *this. W przeciwnym razie musisz wykonać konwersję lub sprawić, że stanie się ona wirtualna.

+0

Nawet jeśli metoda1() zwróci to, nadal nie można wywołać metody 2() z jego wartości zwracanej, ponieważ metoda2() nie jest częścią interfejsu MyBuffer . – wilhelmtell

+0

Czy możesz rozwinąć podejście oparte na metodzie wirtualnej? Nie jestem pewien, czy to mam ... Problem z którym stoję jest kontraktem, implementacja byłaby taka sama (obiekt jest identyczny w pamięci, jeśli X == Y, więc mógłbym po prostu dokonać reinterpretacji). – Krumelur

+0

wilhelmtell, tak, dokładnie o co mi chodziło. Byłeś szybszy :) – Krumelur

1

Sztuką jest mieć MyRegularBuffer::method1 który wywołuje MyBuffer::method1, to sposób na konwersję powstałego MyBuffer<X,X> w MyRegularBuffer<X>:

template<int X> 
class MyRegularBuffer : public MyBuffer<X,X> 
{ 
public: 

    MyRegularBuffer<X>() 
    {} 

    MyRegularBuffer<X>(MyBuffer<X,X>) 
    { 
    // copy fields, or whatever 
    } 

    MyRegularBuffer<X> method2(); 

    MyRegularBuffer<X> method1() 
    { 
    MyRegularBuffer<X> ret(MyBuffer<X,X>::method1()); 
    return(ret); 
    } 
}; 
+0

Dziękuję. Tak, to jest dobry pomysł, ale w moim przypadku nie dodaje nic do rozwiązania @Aarona McDaida, ponieważ muszę ponownie zaimplementować metodę1 (metoda1 to w rzeczywistości szereg metod). – Krumelur

3

pójdę do CRTP tutaj:

template<int X, int Y, class Derived> 
struct MyBufferBase { 
    // common interface: 
    Derived& method1() { return *static_cast<Derived*>(this); } 
}; 

template<int X, int Y> 
struct MyBuffer : MyBufferBase<X, Y, MyBuffer<X,Y> > { 
    // basic version 
}; 

template<int X> 
struct MyRegularBuffer : MyBufferBase<X, X, MyRegularBuffer<X> > { 
    // extended interface: 
    MyRegularBuffer& method2() { return *this; } 
}; 
+0

+1 Dzięki za przypomnienie mi o tym. –

+0

Dzięki za to. To absolutnie dobry pomysł. – Krumelur

+0

Byłbym zaznaczony jako odpowiedź, gdybym mógł dać znak więcej niż jednej odpowiedzi, ale teraz rozwiązanie Aarona McDaida wypada całkiem nieźle, ponieważ moje metody są dość cienkie (jest to klasa opakowania). – Krumelur

Powiązane problemy