Создание шаблона класса C ++ без параметра / в зависимости от типа в операторе if - PullRequest
0 голосов
/ 10 октября 2019

Я написал разные функциональные объекты A, B и C на C ++ и хочу создать экземпляр класса шаблона с одним из функциональных объектов, в зависимости от пользовательского ввода. Но, как кажется, нет способа создать экземпляр только одного из типов в операторе if, правда?

Если я попытаюсь создать экземпляр одного класса с шаблоном в операторе if, компилятор g ++ дает мне"использование необъявленного идентификатора 'bkt'" - ошибка. Если я создаю его заранее, класс уничтожается при выходе из оператора if.

Вот мой код:

class A {
public: void operator()(int a) {}
}; 

class B {
public: void operator()(int a) {}
}; 

class C {
public: void operator()(int a) {}
}; 

template <typename T>
class MyClass {
public:
  MyClass<T>(const std::string &file);
  ~MyClass<T>;

private:
  T m_func;

int doStuff(int a) {
  return m_func(a);
 }
};

int main() {

std::string filename = std::string(argv[1]);
std::getline(std::cin, metric);

if (metric == "A") {
  MyClass<A> foo(filename);
}
else if (metric == "B") {
  MyClass<B> foo(filename);
}
else {
  MyClass<C> foo(filename);
}

int a = 1;
foo.doStuff(int a); // undeclared identifier "foo"

getchar();
return 0;
}

Я знаю, что мне как-то нужно объявить переменную foo,но я надеялся, что с компилятором все будет в порядке, потому что в любом случае произойдет один из случаев оператора if. Очевидно, что это не так, что я могу сделать, чтобы не создавать экземпляры для каждого типа?

1 Ответ

2 голосов
/ 10 октября 2019

Проще говоря: шаблоны помогают вам с полиморфизмом во время компиляции, но вам нужен полиморфизм во время выполнения. Однако их легко смешать.

Сначала вы создаете базовый класс:

struct MyBase {
    ~MyBase(){}
    virtual int doStuff(int a) = 0;
};

Затем вы позволяете шаблонному классу наследовать от него:

template <typename T>
class MyClass : MyBase {
public:
   MyClass<T>(const std::string &file);
   ~MyClass<T>;    
private:
   T m_func;
int doStuff(int a) override {
  return m_func(a);
 }
};

Теперь вы можете иметь указатель на MyBase а внутри кейсов назначить конкретный тип. Я надеюсь, что вы поняли идею и дадите только приблизительный набросок:

std::shared_ptr<MyBase> foo;              // or perhaps a unique_ptr
if (metric == "A") {
    foo = ... create MyClass<A>
} else if (metric == "B") {
    ...
}
foo->doStuff(3);

Ради полноты ... ваш код не сработал, потому что в

if (metric == "A") {
  MyClass<A> foo(filename);
}

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

PS : поскольку все метрики имеют одинаковую подписьЯ бы использовал другой подход, который не требует MyClass в качестве шаблона. В любом случае, MyClass быть шаблоном - это требование.

PPS : Может быть, это просто вопрос формулировки, но ...

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

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

...