【问题标题】:C++ compile time template polymorphismC++ 编译时模板多态性
【发布时间】:2016-07-23 22:31:48
【问题描述】:

我想创建一个应用程序,我可以在其中定义键入的问题列表,然后遍历此列表并要求用户输入答案,将它们存储在另一个列表中,然后检查答案,验证它们并提供结果。

我目前的幼稚方法是这样的:

class Question {
  std::string message;
public:
  Question(const std::string& msg) : message{msg} {}
  std::string& getQuestion(void) const {
    return this->message;
  }
  virtual ~Question(void) = 0;
};

Question::~Question(void) {}

template<class AnswerType>
class Prompt : public Question {
   Prompt(std::string& msg) : Question{msg} {}
   virtual ~Prompt(void) {}
};

class Answer<class T> {
  T answ;
public:
  Answer(const T& answer) : answ{answer} {}
  T getAnswer(void) const {
    return this->answ;
  }
};

我想做类似的事情:

std::list< const Question* > whatToAsk{
  new Prompt<int>{"Your age"},
  new Prompt<std::string>{"Your name"},
  new Prompt<float>{"Your weight"}
};

for(auto q in whatToAsk) {
  Answer< "derived q template parameter" > a{};
  std::cout << q->getQuestion() << ": ";
  std::cin >> a;
  // ... to be continued ...
}

std::list 中存储问题(Prompt)。

但对我来说有问题的部分是我必须使用向下转换(带有运行时检查)、虚拟函数(运行时多态性)或双重分派(同样带有运行时开销)。

我担心类型在编译期间是已知的,因为问题列表将在源代码中硬编码,我想避免运行时开销并实现编译时静态多态性。

我怎样才能做到这一点?也许是某种特征?

【问题讨论】:

  • 您想创建一个对象a,其类型将在运行时已知。我不认为这是可能的。我也认为不可能在 for 循环体内为每次迭代创建不同的类型,除非这些类型是同一继承链的一部分(运行时多态性)。

标签: c++ templates containers


【解决方案1】:

如果你不想使用虚函数/dynamic_casts 你应该

  1. 为每个问题设置不同的类型。
  2. 将它们存储在元组中,而不是列表中。
  3. 使用特殊函数进行迭代(forEachArgument -- 可通过 Google 搜索)。

使用虚函数更简单,除非您有成千上万个问题,否则运行时开销可以忽略不计。

【讨论】:

  • +1 表示元组,完全忘记了它。这将是我的 B 计划,因为我想先尝试可变参数模板。
【解决方案2】:

我不是专家,但也许您可以使用 c++11 可变参数模板,它允许您拥有不同类型的数组,因此不会因向下转换而导致任何开销。

这个链接你可能会感兴趣Create static array with variadic templates

【讨论】:

  • 太棒了。我完全忘记了可变参数模板的力量。会试一试并报告我的进度。
【解决方案3】:

一些零星的笔记,不仅仅是回复

将答案处理部分添加为问题的成员函数。这样你就可以知道问题的类型。类似的东西

void ask()
{
    Answer<T> answer ; 
    std::cint >> a ;
    ....
}

使用 std::shared_ptr 代替普通指针。

可能一个被称为虚拟构造函数的习语作为问题工厂可以帮助构建问题

类似

Question *make_question(int type)
{
   switch (type)
   {
      case 0: return new Prompt<int>() ; 
      case 1: return new Prompt<std::string>() ;
      ...
    }
}

【讨论】:

  • 这是我真正想要避免的。这个设计决定不是“面向未来的”。任何时候你想拥有一个新类型,你都必须将它添加到工厂中。
【解决方案4】:

如果您使用的是 c++14,您可以执行以下操作:

#include <iostream>

template <class FirstQuestion, class... OtherQuestions>
struct QuestionList {
   template <class Functor>
   void foreach(Functor &&functor) {
      functor(FirstQuestion());
      QuestionList<OtherQuestions...> oql;
      oql.foreach(functor);
   }
};

template <class FirstQuestion>
struct QuestionList<FirstQuestion> {
   template <class Functor>
   void foreach(Functor &&functor) {
      functor(FirstQuestion());
   }
};



template <class AnswerType, const char *QuestionString>
struct Question {
   static AnswerType answer;
   static void print_question() {
      std::cout << QuestionString << std::endl;
   }
   static void get_answer() {
      std::cin >> answer;
   }
};

template <class AnswerType, const char *QuestionString>
AnswerType Question<AnswerType, QuestionString>::answer;

constexpr char questionstrings1[] = "lorem";
constexpr char questionstrings2[] = "ipsum";

int main() {
  QuestionList<Question<int, questionstrings1>, Question<float, questionstrings2> > a;
  a.foreach([](auto x){ x.print_question(); x.get_answer(); });
}

要循环访问答案,您可以简单地:

a.foreach([](auto x){ /*doing something with x.answer*/ };

PS 请记住,提示用户回答会扼杀非运行时多态性的潜在效率......

【讨论】:

  • 感谢您的提示。不幸的是,我目前无法访问 C++14 编译器,并且 lambda 函数的自动类型不可用,但我一定会牢记这一点!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-10
  • 1970-01-01
  • 2015-07-27
  • 1970-01-01
  • 2013-03-28
  • 1970-01-01
相关资源
最近更新 更多