Как реализовать эквивалент Java Generics Самоограниченные типы в шаблонах C ++ - PullRequest
0 голосов
/ 15 декабря 2011

У меня есть небольшая коллекция алгоритмов на Java для игры в несколько пошаговых игр, таких как TicTacToe, Othello, Checkers и т. Д. Я делаю это, используя Java Generics (самоограниченные типы), чтобы иметь возможность использовать одни и те же алгоритмыбез необходимости менять их специально для каждой игры.Причина, по которой я использую самоограниченные типы, здесь не показана, но необходима для функций оценки.

public interface Game<GAME extends Game<GAME>> {
    GAME copy();
    int getCurPlayer();
    ...
}

public class TicTacToe implements Game<TicTacToe> {
    ...
    @Override
    public TicTacToe copy() {
        ...
    }
    @Override
    public int getCurPlayer() {
        ...
    }
    ...
}

Сегодня, просто для обучения, я попытался переместить свой Java-код на C ++, используя C ++шаблоны.

Это был мой подход, и, очевидно, он не работал.

Game.h

template <typename T>
class Game
{
    public:
        virtual T copy() const = 0;
        virtual int cur_player() const = 0;
        ...
};

TicTacToe.h

class TicTacToe : public Game<TicTacToe>
{
public:
    virtual TicTacToe copy() const;
    virtual int cur_player() const;
    ...
};

TicTacToe.cpp

TicTacToe TicTacToe::copy() {
    ...
}

int TicTacToe::cur_player() {
    ...
}

При попытке компилировать ошибкиЯ получаю:

out-of-line definition of 'copy' does not match any declaration in 'TicTacToe'

out-of-line definition of 'cur_player' does not match any declaration in 'TicTacToe'

... и то же самое для каждой из других чисто виртуальных функций.

Ответы [ 3 ]

5 голосов
/ 15 декабря 2011

Ваши определения должны иметь к ним const. CRTP, как он известен в C ++ (Curiously Recurring Template Pattern), является совершенно допустимым C ++.

Однако здесь нет необходимости в virtual, CRTP используется для статического распределения функций и автоматической реализации функциональности.

template <typename T>
class Game
{
    T& crtp_cast() { return *static_cast<T*>(this); }
    const T& crtp_cast() const { return *static_cast<const T*>(this); }
public:
    T copy() const { return crtp_cast(); }
    int cur_player() const { return crtp_cast().cur_player(); }
    ...
};

Обратите внимание, что в этом случае производный класс не нуждается в реализации функции "копирования", поскольку конструктор копирования будет автоматически вызываться функцией "копирования". Тем не менее, в общем случае, поскольку шаблоны напечатаны по типу утки, в этом нет необходимости, и обычно вы просто используете стандартный шаблон. В отличие от Generics Java, шаблоны C ++ не имеют никакого отношения к наследованию - типы, с которыми вы можете создавать экземпляры, не должны наследоваться от общего интерфейса.

template<typename Game> void f(const Game& g) {
    std::cout << g.cur_player();
}
class X {
public:
    int cur_player() const { return 1; }
};
class Y {
public:
    int cur_player() const { return 2; }
};
int main() {
    f(X());
    f(Y());
}
1 голос
/ 15 декабря 2011

Вместо использования шаблонов / шаблонов, я бы просто передавал указатель на игру из копии и тому подобное, а затем динамически приводил его вниз, если вам действительно нужно.

Например:

Game.h:

class Game
{
public:
    virtual Game* copy() const = 0;
    virtual int cur_player() const = 0;
    ...
};

tictactoe.h:

class TicTacToe : public Game
{
public:
    virtual Game* copy() const;
    virtual int cur_player() const;
    ...
};

tictactoe.cpp:

Game* TicTacToe::copy()
{
    ...
}

int TicTacToe::cur_player()
{
    ...
}

Создайте свою игру так:

TicTacToe ttt;
Game* game = &ttt;
Game* nextGame = game->copy(); // this will call TicTacToe::copy

Если вам нужно вызывать специальные методы TicTacToe, либо добавьте их в игру, либо попробуйте понизить рейтинг:

TicTacToe* ttt = dynamic_cast<TicTacToe*>(game);
ttt->TTTSpecificMethod();
0 голосов
/ 09 мая 2014

Самоограничивающиеся дженерики (Java) эквивалентны любопытно повторяющемуся шаблону шаблона (C ++) с помощью нескольких модификаций.

template <class T>
class A
{
private:
  explicit A(A *temp) {}

public:
  explicit A() : A(static_cast<T*>(this)) {}
  virtual A* function(T *parameter) = 0;
};

class B final : public A<B>
{
public:
    B* function(B *parameter) { return new B(); }
};

class C final
{
};

class D final : public A<C>
{
public: // It occurs compile-time-error
  D* function(C *parameter) { return new D(); }
};

class E final : public A<E>
{
public: //"override" specifier will not be accepted due to signature does not match!
    E* function(E *parameter) override { return new E(); }
};

class F final : public A<B>
{
public: //You can also assign another relative type but type-self
    F* function(B *parameter) { return new F(); }
};

int main()
{
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...