我听说C ++模板在使用之前不会产生错误。这是真的吗?有人可以解释一下他们是如何运作的吗?
答案 0 :(得分:6)
模板遵循两阶段编译模型。
struct X{
private:
void f(){}
};
template<class T> void f(T t){
int; // gives error in phase 1 (even if f(x) call is commented in main)
t.f(); // gives error only when instantiated with T = X, as x.f() is private, in phase 2
}
int main(){
X x;
f(x);
}
答案 1 :(得分:1)
编译时会生成编译器错误。它们是作为模板参数传递的每个实际参数单独编译的(这与Java Generics不同),例如,如果我有:
template <typename T> class foo { ... }
和
int main() {
foo<char> c;
foo<int> i ;
}
模板foo
被编译两次,一次用于字符,一次用于整数。
如果您从未(直接或间接)实例化或使用模板foo
,则不会编译它,您也不会看到任何编译器错误。
编译完成后,它们只是“正常”的C ++代码,就像任何代码一样,都会产生运行时错误。
答案 2 :(得分:0)
来自here,
从...的角度来看 编译器,模板不正常 功能或类。他们是 按需编译,意思是 代码模板函数不是 编译直到实例化 具体的模板参数是 需要。那一刻,当一个 实例化是必需的, 编译器生成一个函数 专门针对那些论点来自 模板。
希望有所帮助......
答案 3 :(得分:0)
从概念上讲,处于最高层次
template <Type value, class Y, ...> ...fn-or-class...
可能与
相比有用#define FN_OR_CLASS(VALUE, TYPE_Y, ...) \ ...fn-or-class...
两者基本上都等到调用/实例化然后替换指定的类型和值来生成定制代码,并对这些值进行完全编译时优化。但是,模板与#defines的不同之处在于它们是适当的编译阶段结构,可以包含在命名空间中,必须满足词法分析器,并且在看到第一个实例时不会生成所有类模板 - 而是生成函数根据需要。
当编译器第一次遇到模板时,它会粗略地检查模板的内容是否适用于某些假设的实例化。稍后,当遇到特定的实例化时,对于类模板,仅进一步检查使用的函数,以确保可以使用正在使用的特定参数对它们进行编译。这确实意味着类模板可以出现 - 用于某些有限的用途 - 以支持使用特定参数进行实例化,但是如果您开始在模板API中使用其他一些函数,那么突然您可以发现它无法使用假定的编译 - 合适的参数......可以迫使你在一天的晚些时候重新设计你的用法。这是C ++ 0x计划引入Concepts的原因之一:它们优雅地允许模板检查参数是否满足模板的所有期望 - 如果它们允许任何实例化,那么用户可以假设模板的完整API可以使用。
template <class T> struct X { void f() { } void g() { T::whatever(); } // only error if g() called w/o T::whatever }; int main() { X<int> x; x.f(); // x.g(); // would cause an error as int::whatever() doesn't exist... }
SFINAE(替换失败不是错误)技术然后允许编译器基于实际的实例化模板参数在多个几乎匹配的函数之间进行选择。这可以用来实现基本的编译时内省,例如“这个类有成员函数fn(int)吗?”。