C++编译时模板多态性

C++ compile time template polymorphism

本文关键字:多态性 编译 C++      更新时间:2023-10-16

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

我目前天真的做法是这样的:

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 ...
}

将问题(Prompt<T>)存储在std::list<const问题*>

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

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

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

如果你不想使用虚拟函数/dynamic_cast,你应该

  1. 把每个问题都做成不同的类型
  2. 将它们存储在元组中,而不是列表中
  3. 使用一个特殊的函数(forEachArgument——它可以在谷歌上搜索)进行迭代

使用虚拟函数做这件事要简单得多,除非你有成千上万的问题,否则运行时开销可以忽略不计。

我不是专家,但也许你可以使用c++11可变模板,这将允许你有一个不同类型的数组,因此不会因下变频而导致任何开销。

这个链接可能会让你感兴趣用可变模板创建静态数组

一些稀疏的音符,而不是一个回复

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

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>() ;
      ...
    }
}

如果您使用的是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意识到,提示用户回答会扼杀非运行时多态性的潜在效率。。。