我有以下方案:
struct Baz {};
struct Qux {};
struct Base {
virtual ~Base() {}
virtual void foo() = 0;
};
template<typename T> struct Identity { static bool const value = false; };
template<typename T> void bar(T) { static_assert(Identity<T>::value, "Busted!!!"); }
template<> void bar<Baz>(Baz) {}
template<typename T>
struct Derived : Base {
T m;
void foo() { bar(m); }
};
int main() {
Base *b0 = new Derived<Baz>;
b0->foo();
Base *b1 = new Derived<Qux>;
(void) b1;
}
也就是说,我有一个纯虚拟类Base
和一个继承自Derived
的模板类Base
,并根据需要覆盖纯虚函数foo
。现在,在foo
内,我调用了函数模板bar
。 bar
具有类Baz
的专门化,但不适用于类Qux
。在main
时,我试图实现Derived<Baz>
一切对象的确定。但是当我尝试实现Derived<Qux>
编译器点击static_assert
的对象时。
有没有办法转换我的代码,只有在Derived<Qux>
被调用时编译器才会在Derived<Qux>::foo()
中命中静态断言。
即,实现Derived<Qux>
的对象将通过:
Base *b1 = new Derived<Qux>;
但是后来在代码中程序员试图调用:
b1->foo(); // compile error static assert
答案 0 :(得分:10)
标准在[temp.inst]/9说了一个有趣的事情:
实现不应隐式实例化函数 模板,变量模板,成员模板,非虚拟成员 function,成员类,类模板的静态数据成员,或 constexpr if语句的子语句,除非这样的实例化 是必须的。没有具体说明是否实施 隐式实例化类模板的虚拟成员函数 如果虚拟成员函数不会被实例化。
实例化虚拟功能的决定由实现决定,但前提是不需要。因此,我们面临的问题是:根据标准本身何时需要定义?
答案是[class.virtual]/11和[temp.inst]/2:
在类中声明的虚函数应该在该类中定义或声明为纯,或者两者都是;无需诊断
类模板特化的隐式实例化会导致声明的隐式实例化,而不会导致类成员函数的定义,默认参数或noexcept-specifiers
因此,类模板的任何实例化都将实例化Derived::foo
的声明,这通过连锁反应需要定义。因此,定义也必须实例化,如果可用的话。
实现可以在第一个引用的段落中给出它的余地的唯一方法是Derived::foo
是否也是纯虚拟的。例如,Clang和GCC都可以做到这一点。那当然可能对你的帮助有限。
所以长话短说,只要功能是虚拟的(而不是纯虚拟的),它就是一个不起作用的。
答案 1 :(得分:1)
@StoryTeller提供了详细的答案,参考了规范等,但我会回过头来回答你的问题并询问你真正想要做什么。正如问题所写的那样,很明显,答案是“#34; no&#34;因为您要求在运行时才能确定的编译时错误。 E.g:
Base *b;
if (user_input() == 42) {
b = new Derived<Baz>();
} else {
b = new Derived<Qux>();
}
b->foo();
您是否希望此情况下出现编译器错误?如果是这样,您需要定义您认为Qux::foo
应被视为&#34;被调用的条件。&#34;目前,编译器假定在实例化类中定义为虚拟的方法被调用。显然你想要一些不那么保守的东西,但是什么呢?
如果您有更具体的编译时类型信息,则可以在运行时捕获错误。 E.g。
Derived<Qux> d = new Derived<Qux>();
d->foo();
如果foo
是非虚拟模板化方法,则可以在编译时验证基类型,然后调度到虚方法。 (可能需要更改foo的签名才能以某种方式使用该类型。)
更好的解决方案是将接口中的不同类型的功能分解为不同的类,并引入一种机制来获取给定具体类的特定接口。如果您具有手头的具体类型类并且在运行时具有动态查找接口,则可以在编译时检查。