从文件中获取模板参数

Get template parameter from file

本文关键字:参数 获取 文件      更新时间:2023-10-16

我有两个类:

  1. 非类型模板类Foo
  2. 一个非模板类Bar,它包含Foo的一个成员,其中的模板参数应该从文件中读取

据我所知,这不起作用,因为非模板化类的模板参数是在编译时设置的,所以它们需要是静态常量。我不知道如何实现这一点,因为在我提供参数值之前,必须进行一些评估(即读取文件、提取值)。尽管如此,有没有办法实现类似的目标?

我希望避免模板化第二个类,因为用户(理论上)不知道该文件,应该在执行程序时查找该文件。该文件在运行时不会更改。

因此,情况是这样的:

// templated class
template <int A>
class Foo {
  public:
      Foo() : A_(A) {};
      void print () { std::cout << "The value of A is : " << A_ << std::endl; }
  private:
      int A_;
};
// non-templated class
// This won't work, just showing the idea:
class Charlie {
    public:
        Charlie() {
            A = loadFromFile("myfile");
        }
        void print () { 
            std::cout << "Called from C: " << std::endl;
            C_.print(); 
        }
    private:
        int A;
        Foo<A> C_;
};

谢谢!

您不能从文件中读取模板参数,也不能在运行时以其他方式确定。

模板参数,本质上需要在编译时推导/计算。别无选择,对不起!

模板参数的思想是,它们可以在编译时进行优化,因此不能推迟到运行时解决。

在您的示例中,答案很简单。使Foo不是模板。

Foo在您的案例中作为模板没有任何好处。

从表面上看,这似乎是对你的问题的法律误读,它触及了问题的核心。您可以重构代码中在运行时发生变化的部分,而这些部分不是模板参数。您正在从文件中读取一个int:它是一个运行时参数。您可以通过接口、类型擦除等方式在编译时和运行时完成大部分可以完成的工作。理论上,你可以构建一个与编译时一样强大的运行时类型系统,但我们称之为一种新语言。)

编译时模板为您提供了对编译时所知内容的检查,并提供了一些优化机会(编译时已知更多)和安全性(同上)。

当您不知道模板参数的值、范围或任何东西时,这些检查都是没有意义的,因此是不必要的。使参数成为运行时参数,而不是编译时参数。

你正在做的事情的一个具体例子可以带来一个具体的解决方案。

struct Foo {
  Foo(int A) : A_(A) {};
  void print () {
    std::cout << "The value of A is : " << A_ << std::endl;
  }
  int A_;
};
struct Charlie {
  Charlie():C_{loadFromFile("myfile")}{}
  void print () {
    std::cout << "Called from C: " << std::endl;
    C_.print();
  }
  Foo C_;
};

这与你要求的行为完全匹配。不是你要求的方式,但考虑到实际问题,这种方式毫无意义。

"魔术开关"可以用于从运行时调度到编译时代码。双重调度更为棘手,因为它是在运行时进行的,您需要类似的折衷方案。类型擦除(如sts::function)允许您将状态需求抽象到行为类似"相同"类型的干净接口中。生成的跳转表保持低成本。

不幸的是,C++元编程能力对于这种进程来说太弱了。

虽然你的要求没有错(即使用真正的工具(如打开文件和读取一些数据)进行编译时计算),但这在当前(以及IMO和未来)C++中是完全不可能的。

此外,在您的示例中,您似乎需要在运行时读取值,并且仍在考虑编译时和运行时的独立阶段。我能想到的避免在"运行时"生成代码的唯一解决方案是将Bar作为基类,并为您需要传递的数字的所有可用值预先实例化派生类。这样,您就可以选择特定的派生类在运行时实例化。然而,请注意,这很可能是C++的一个无意义的解决方案,如果尝试这样做,也会有严重的代码膨胀风险。

然而,在C++中,即使操作需要在编译时进行,也不可能读取文件,并且您不要求在运行时生成代码。

要解决这类元编程问题,唯一的解决方案是从单独的程序生成代码。我个人发现Python是生成C++的一个很好的工具,但任何具有一定文本处理能力的语言都可以做到这一点。

C++元编程只是关于模板,即"愚蠢"地替换函数和类骨架。由于某些原因,添加了一些功能,再加上该语言极其复杂的语法规则,恰好为这种非常原始的方法带来了(IMO非自愿的)图灵完整性。

这本身并不坏,但不幸的是,由于一些奇怪的心理影响(对我来说无法解释),这也意味着任何增加真正元编程能力的尝试都被放弃了,取而代之的是,所有C++大师都继续试验这个错误构建的新玩具。

您可以使用递归来模拟循环,使用SFINAE和专门化来模拟条件,使用递归类型列表来模拟数据结构。。。他们还难以置信地强调了编译器,并获得了例如提高模板实例化递归堆栈的能力,以便能够做更多的技巧。

这种对无意义谜题的热爱指导了所有"现代"C++对元编程的研究,因此,几十年后,我们仍然无法在编译时(甚至在运行时…C++也没有反射,因为这些人还没有找到使用SFINAE模拟的方法)完成琐碎的任务,比如从文件中读取或枚举类的成员。

您在标准库中有编译时有理算术,但是。。。