【问题标题】:Instantiate C++ template class without parameter / depending on type in if-statement根据 if 语句中的类型实例化不带参数 / 的 C++ 模板类
【发布时间】:2020-02-08 00:22:52
【问题描述】:

我用 C++ 编写了不同的函数对象 A、B 和 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 语句的一种情况无论如何都会发生。显然不是,那么我该怎么做才能不实例化每种类型?

【问题讨论】:

  • 真的必须是模板参数吗?您的不同指标似乎都是同一类型,这仅仅是由于示例的简化吗?如果不是,我建议删除模板。在运行时选择模板实例化并不是模板真正发挥作用的地方。模板必须在编译时实例化
  • 不好意思马虎,它们当然不是同类型的,但是简单的重构就可以做到,而与模板保持一致则需要更多的努力
  • 您可以在度量评估之前声明int a = 1;,然后在每个if 语句中调用foo.doStuff(a);(我知道这是代码重复,但如果没有重构,我看不到另一种方法) . 注意:您必须在foo.doStuff() 调用中提供a 而不是int a
  • @foreknownas_463035818 使用模板是要求的一部分。 Annnd 是的,这些函数实际上做了非常不同的事情(它是声明哪个字符串度量 (en.wikipedia.org/wiki/String_metric) 用于自动更正)...

标签: c++ c++11 g++


【解决方案1】:

简单来说:模板可以帮助您实现编译时多态性,但您想要的是运行时多态性。但是,将它们混合起来很简单。

首先你创建一个基类:

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 的指针,并在 if-cases 中分配具体类型。我希望你能得到这个想法,并且只会给出一个粗略的草图:

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:也许只是措辞的问题,但是……

那么我该怎么做才能不实例化每种类型?

对此你无能为力。模板在编译时被实例化。如果您想在运行时选择一个实例化,您需要将它们实例化。在上面的代码中(一旦你填补了漏洞),每个可能的指标都会有一个实例化,但它只会创建一个实例。

【讨论】:

  • 非常感谢。我想,我快到了,但它仍然给我“没有可行的重载'='”-error:
  • 说:候选函数不可行:第一个参数没有从 'MyClass *' 到 'const std::__1::shared_ptr' 的已知转换
  • 我能否以某种方式强制转换指针,或者将其作为解决此问题的唯一方法来重载 = 运算符?
  • @NinellOldenburg 稍后我会将其编辑为更完整的示例...
  • 谢谢!我添加了一些东西,castet 指针,它工作得很好!现在问题出在覆盖的某个地方,我知道我做错了什么......
猜你喜欢
  • 2011-09-23
  • 1970-01-01
  • 2013-02-05
  • 1970-01-01
  • 2017-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多