Подход для установки параметра конструктора Dynami c внутри класса шаблона - PullRequest
1 голос
/ 13 марта 2020

Допустим, у нас есть шаблонный класс (я назову его TileCreator ), основанный на другом классе Tile . В некоторых случаях конструктору класса Tile требуются параметры для инициализации. Теперь класс TileCreator будет создавать множество объектов Tile , поэтому я хотел бы сохранить необходимые параметры в виде переменной-члена в TileCreator .

Есть ли хороший подход для этого? Как я могу сообщить своему шаблонному коду, какие типы и сколько этих параметров?

Мой единственный подход состоит в том, чтобы использовать указатель void *, который указывает на него, однако тогда мне потребуется вручную выделить и освободить память каждый раз?

В идеале это должно работать так:

template<class Tile> class TileCreator {
public:
    void CreateNew() {
        Tile tile(par);
    }
    void setPar(void* par) {
        this->par = par;
    }
private:
    void* par;
};

class Cake {
public:
    Cake(int sugar, bool chocolate) {
        // ...
    }
};

int main() {
    TileCreator<Cake> baker;
    // baker.setPar(over 9000, true); obviously doesnt work like this
    return 0;
}

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

Я надеюсь, что есть более элегантное решение для этого!

Ответы [ 2 ]

0 голосов
/ 13 марта 2020

Вы можете использовать std::tuple для хранения аргументов и std::apply для вызова с ними конструктора Tile.

template <typename T, typename... Tn>
class Factory
{
private:
    std::tuple<Tn...> args;

public:
    template <typename... Args>
    explicit Factory(Args&&... args)
    : args(std::forward<Args>(args)...)
    {
    }

    T operator()() const {
        return std::apply([](Tn const&... args)
        {
            return T(args...);
        }, args);
    }
};

template <typename T, typename... Args>
auto make_factory(Args&&... args)
{
    return Factory<T, std::decay_t<Args>...>(std::forward<Args>(args)...);
}

Используется как Итак:

int main()
{
    auto const make_tile = make_factory<Tile>(1, 2.0f, true);
    make_tile();
    make_tile();
}

Вы можете даже заменить класс Factory на лямбду и std::bind для упрощения реализации.

template <typename T, typename... Args>
auto make_factory(Args&&... args)
{
    return std::bind([](auto&&... args)
    {
        return T(std::forward<decltype(args)>(args)...);
    }, std::forward<Args>(args)...);
}

В C ++ 20 вы можете использовать std::bind_front, что также позволит вам передавать дополнительные аргументы при создании.

template <typename T, typename... Args>
auto make_factory(Args&&... args)
{
    return std::bind_front([](auto&&... args)
    {
        return T(std::forward<decltype(args)>(args)...);
    }, std::forward<Args>(args)...);
}
int main()
{
    auto const make_tile = make_factory<Tile>(1, 2.0f);
    make_tile();
    make_tile(true);
    make_tile(false, "hello");
}
0 голосов
/ 13 марта 2020

Отредактировал мой ответ на следующее:

Это имеет недостаток: вам нужен один экземплярный объект шаблона в Creator, который будет использоваться при создании других объектов при вызове конструктора копирования. Вам, конечно, нужно позаботиться об управлении указателем (возможно, использовать умный указатель)

template <typename Tile>
class Creator {
  private:
    Tile *tileTemplate;
  public:

    template <typename... Args>
    Creator(Args&& ... args) {
      tile = new Tile(std::forward<Args>(args)...); 
    }
    void createNew() {
      Tile tile = *tileTemplate;
    }
    ~Creator() {
      delete tileTemplate;   
    }
};

class A {
  public:
  A(int a, int b):a(a) {} 
};

class B {
  public:
  B(const std::string& a):a(a) {}
};

int main()
{

  Creator<A> ca(1,2);
  ca.createNew();

  Creator<B> cb("Hello");
  cb.createNew();

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...