我有两个班级:
据我所知(我认为),这不起作用,因为非模板类的模板参数是在编译时设置的,因此它们需要是静态的。 我不知道如何实现这一点,因为在我可以提供参数值之前必须进行一些评估(即读取文件,提取值)。 尽管如此,有没有办法实现类似的东西?
我想避免模板化第二个类,因为用户(理论上)不知道该文件,应该在执行程序时查找该文件。该文件在运行时不会更改。
情况是这样的:
// 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_;
};
谢谢!
答案 0 :(得分:3)
您无法从文件中读取模板参数,或在运行时以其他方式确定。
模板参数,本质上需要在编译时推导/计算。没别的办法,对不起!
模板参数的概念是它们可以在编译时进行优化,因此不能将它们解析为运行时。
答案 1 :(得分:1)
不幸的是,C ++元编程功能对于这种过程来说太弱了。
虽然你所要求的并没有什么不对(即使用打开文件和读取某些数据等真实工具进行一些编译时计算),但对于当前(以及IMO未来的)C ++来说,这是完全不可能的。
在您的示例中,您似乎需要在运行时读取值,并且您仍在考虑编译时和运行时分离阶段。
我能想到的唯一解决方案是避免在“运行时”生成代码,使得Bar
成为基类,并为需要传递的数字的所有可用值预先实例化派生类。这样您就可以选择要在运行时实例化的特定派生类。但请注意,这很可能是C ++的无意义解决方案,如果尝试这样做,您也会面临严重的代码膨胀风险。
然而,在C ++中,即使操作需要在编译时发生并且您不要求在运行时生成代码,也无法读取文件。
要解决这种元编程问题,唯一的解决方案是从单独的程序生成代码。我个人认为Python是一个很好的工具来生成C ++,但任何具有一些文本处理能力的语言都可以解决问题。
C ++元编程只是关于模板,即在功能和类骨架中用于替换的“愚蠢”。由于某些原因,添加了一些功能,结合语言的极其复杂的语法规则,使(IMO不由自主地)turing-completeness-power成为这种非常原始的方法。
这本身并不是坏事,但不幸的是,由于一些奇怪的心理影响(对我来说莫名其妙),这也意味着任何增加真正的元编程能力的尝试都被删除了,而所有的C ++大师都继续尝试这个新的玩具这是错误建立的。
你可以使用递归模拟循环,SFINAE和专门化来模拟条件,递归类型列表来模拟数据结构......他们还强调编译器难以置信并获得例如为模板实例化提高递归堆栈能够做更多的技巧。
这种对无意义谜题的热爱是指导所有“现代”C ++关于元编程的研究,因此我们在这里,几十年后,仍然无法做一些简单的任务,比如从文件中读取或在编译时枚举类的成员(甚至在运行时...... C ++也没有反射,因为那些人还没有找到使用SFINAE来模拟它的方法)。
标准库中有compile-time rational arithmetic,但是......
答案 2 :(得分:1)
在您的示例中,答案很简单。使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_;
};
与您要求的行为完全匹配。不是你提出的方式,但考虑到实际问题,这种方式毫无意义。
&#34;魔术开关&#34;可用于从运行时调度到编译时间代码。双重调度比在运行时更棘手,并且您需要类似的妥协。类型擦除(如sts::function
)允许您将状态需求抽象为干净的界面,其行为类似于&#34;相同的&#34;类型。生成的跳转表可以降低成本。