Как обобщить создание объектов в C ++ с помощью шаблонов с переменными числами? - PullRequest
0 голосов
/ 04 января 2019

Я пытаюсь создать универсальный фабричный метод в C ++, который может создать экземпляр одного из множества (но конечного числа) объектов. Каждый из объектов требует создания различных типов параметров, поэтому я хочу, чтобы метод каким-то образом выводил желаемый тип, и не хотел, чтобы пользователь указывал его явно.

Вот код для иллюстрации того, что я пытаюсь сделать:

#include <iostream>
using namespace std;

class A {
public:
    A(int x, int y) {
        cout << "A(" << x << ", " << y << ")\n";
    }
};

class B {
public:
    B(float a, float b) {
        cout << "B(" << a << ", " << b << ")\n";
    }
};

template<typename T, typename... Ts>
T * Make(Ts... vs) {
    puts(__PRETTY_FUNCTION__); // __FUNCSIG__ or __PRETTY_FUNCTION__
    return new T{ vs... };
}

Теперь код, скажем, функции main может создавать объекты типа A * и B *, вызывая Make следующим образом:

A *a = Make<A>(3, 4);
B *b = Make<B>(3.14f, 6.28f);

Можно ли как-то расширить этот код, чтобы другие функции могли вызывать Make без необходимости явно указывать, хотят ли они экземпляр A * или B *? Например,

A * a = Make(3, 4); // (int, int)
B * b = Make(3.14f, 6.28f); // (float, float)

Я понимаю, что шаблоны функций создаются с использованием вывода типа аргумента, а возвращаемый тип не участвует в этом. Однако компилятор не будет выполнять никаких преобразований типов. Так что Make(int, int) определенно отличается от Make(float, float), и я хочу иметь возможность использовать это для сопоставления определения функции с правильным типом возвращаемого значения.

Вот что я попробовал:

  • определение явной реализации

    template A * Make(int x, int y);
    
  • определение специализации

    template<>
    A * Make<A, int, int>(int x, int y);
    

Оба не сработали, как ожидалось. Любые идеи о том, как этого можно достичь?

Ответы [ 2 ]

0 голосов
/ 05 января 2019

Вы можете предоставить необходимую информацию фабрике, набору классов для создания и необходимым аргументам (*).

(*): (Примечание: если у ваших классов правильные свойства, вы можете даже автоматически определить аргументы конструктора с помощью magic_get .

template <typename ... Sigs> struct Maker : Maker<Sigs>...
{
    using Maker<Sigs>::operator ()...;

    // Do we want to accept conversions or not in non ambiguous cases ?
    //template <typename ... Ts> operator()(Ts/*&&*/...) const = delete;
};

template <class C, typename ... Args> struct Maker<C(Args...)>
{
    C operator()(Args... args) const {
        puts(__PRETTY_FUNCTION__); // __FUNCSIG__ or __PRETTY_FUNCTION__
        return C(std::forward<Args>(args)...); // not {} to avoid initializer_list constructor
    }
};

А потом заводской экземпляр:

constexpr Maker<A(int, int), B(float, float)> Make{};

При использовании:

A a = Make(3, 4); // (int, int)
B b = Make(3.14f, 6.28f); // (float, float)

Демо

0 голосов
/ 04 января 2019

Я написал вспомогательный шаблон Maker, который регистрирует, какие классы разрешены, хотя я точно не знаю, как отключить базовый шаблон:

template <typename... Ts>
struct Maker {
    using type = void;
};

template <>
struct Maker<int, int> {
    using type = A;
};

template <>
struct Maker<float, float> {
    using type = B;
};

template<typename... Ts, typename T=typename Maker<Ts...>::type>
T * Make(Ts... vs) {
    puts(__PRETTY_FUNCTION__); // __FUNCSIG__ or __PRETTY_FUNCTION__
    return new T{ vs... };
}

int main() {
    A * a = Make(3, 4); // (int, int)
    B * b = Make(3.14f, 6.28f); // (float, float)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...