请考虑以下代码(live version),其中我在ODR使用它的功能模板struct S
之前声明了Use
。 S
是在函数之后 ,但在Use
的任何实例化之前立即定义的。
struct S;
template<typename T> void Use(S& s, const T& t) { // ODR-use S
s.Use(t); // error: member access into incomplete type 'S'
}
struct S {
template<typename T> void Use(const T&) { }
};
int main() {
auto s = S{}; Use(s, 2); // Instantiation of Use<...>
}
这不会编译,因为显然S的定义对于Use
函数是不可见的。
根据我在阅读§13.9.1 of the latest working draft之后对函数模板的理解,这应该不成问题,因为在实例化函数模板时所有类型都已完全定义。
尽管,对我来说,似乎所有依赖类型都可以访问实例化时可用的定义,而非依赖类型将使用函数定义中可用的版本。不幸的是,我无法在定义此行为的标准中找到相应的段落。
我错过了标准的哪一部分来解释这种行为?
要变通解决此问题,我可以使用IIFE“刷新”当前定义:
// This is OK:
template<typename T> void Use(S& s, const T& t) {
[&](auto& s_) { s_.Use(t); }(s);
}
或者通过为S设置从属类型别名:
// This is also OK:
template<typename T, typename SS = S> void Use(SS& s, const T& t) {
s.Use(t);
}
所以现在我想知道,为什么实例化的模板不总是可以访问任何类型的最新定义,而不仅仅是依赖类型?如上所示,您可以使用立即求值的lambda强制编译器更新任何类型-为什么不自动完成此操作?
答案 0 :(得分:1)
前向声明只是对编译器的承诺,“是的,我保证在链接时会确实存在这种类型的 ”。它不提供类型的声明。因此,对于返回值,指针或对该类型的引用来说已经足够了,但是如果您实际上要调用函数或访问成员变量,那么需要的完整声明和正向声明就不再有效了足够-因此您的编译错误。