【发布时间】:2019-09-19 14:37:33
【问题描述】:
看了很多类似话题的帖子,想了想,还是不明白为什么禁止实现模板虚函数。 在我看来,这种情况与将静态多态性与动态多态性混合无关,而是在编译时使用函数的模板微分,然后在运行时对每个单独创建的函数使用动态多态性-时间。
考虑这段代码:
class parrent{
public:
virtual float function(float value)const{
return value;
}
virtual double function(double value)const{
return value;
}
virtual long double function(long double value)const{
return value;
}
virtual ~parrent() = default;
};
class a_child:public parrent{
public:
float function(float value)const override{
return value + 1.5;
}
double function(double value)const override{
return value + 1.5;
}
long double function(long double value)const override{
return value + 1.5;
}
};
显然这段代码是可以的,并且会达到预期的结果。 但是使用模板重写类似的代码:
class parrent{
public:
template<typename t__>
virtual t__ function(t__ value)const{
return value;
}
virtual ~parrent() = default;
};
class a_child:public parrent{
public:
template<typename t__>
t__ function(t__ value)const override{
return value + 1.5;
}
};
不允许。
我不是编译器设计者,但根据我的阅读,编译器将从虚函数创建一个查找表,并使用它们在运行时启动适当的函数,这与他们在模板函数的情况下所做的不同.对于在编译时为使用模板函数而给出的任何模板参数集,编译器将创建一个唯一的函数。 对于这个例子,编译器可以在编译时检测模板参数,只需查看这个虚拟模板函数是如何在整个程序中使用的。现在请考虑 main 函数:
int main() {
parrent* a;
parrent* b;
a = new parrent;
b = new a_child;
std::cout<< a->function(1.6f) << std::endl;
std::cout<< a->function(1.6) << std::endl;
std::cout<< a->function(1.6L) << std::endl;
std::cout<< b->function(1.6f) << std::endl;
std::cout<< b->function(1.6) << std::endl;
std::cout<< b->function(1.6L) << std::endl;
delete a;
delete b;
return 0;
}
在这里,编译器将看到该函数被用于浮点值,一次用于双精度值,一次用于长双精度值,因此在任何情况下,它都可以使用适当的模板参数轻松创建正确的函数。 最终会有3个单独的虚函数,而不仅仅是一个虚函数。 如果我们有一个无法从函数输入中推断出模板参数的函数,例如
template<typename t__>
virtual t__ function(int value){return value;}
然后用户可以自己给参数,比如:
object_pointer->function<double>(1234);
这些做法正是在任何模板函数的情况下已经使用的,那么为什么虚拟函数会有所不同!
我能想到的对这种做法的唯一警告是,模板虚函数是从子对象实例化的,而不是从父对象或指针实例化的。 好吧,即使在那种情况下,也可以应用相同的做法来创建不同的虚拟功能。或者,由于没有使用它们的虚拟性,它们可以成为正常的个体功能。
从答案和 cmets 看来,这种方法可能存在严重问题,这对其他人来说是显而易见的,所以请耐心等待并帮助我理解它。
我猜答案中提到的问题与编译器和/或链接器无法知道它应该为一个类相对于其余代码或不同的代码生成多少(和什么类型)vtables 有关它可能面临的翻译单元。
好吧,可以说它可以生成一个未完成的 vtables 列表并随着它的进行扩展它。在实例化具有虚拟(非模板)函数的模板类时,可能已经发生在动态链接的情况下以两个 vtable 或同一类的两个不同实例结束的问题。 所以看来编译器已经有了规避这个问题的方法!
首先不要忘记,对于 c,方法或类的非静态函数只不过是需要一个对象作为其参数之一的简单函数,所以不要将类视为一些复杂的代码。
其次,让我们不要被编译器和链接器的方式以及今天不工作的东西所迷惑。该语言应该是标准的,而不是编译器生成可执行文件的方式!让我们不要忘记标准 c++ 17 中还有许多甚至 GCC 都没有涵盖的特性!
请用逻辑而不是编译器和/或链接器的工作方式向我解释有什么问题?
【问题讨论】:
-
您是在学习/研究编译器/语言,还是您真正想要实现的目标?
-
考虑有多少种方式会破坏动态(运行时)链接。一个 DLL 中使用的
parrent可能无法在另一个 DLL 中使用,因为它们具有不同的 vtable。 -
“请根据逻辑而不是编译器和/或链接器的工作方式向我解释” - C++ 标准的一个重要方面是可实现性。所以逻辑是标准拒绝了难以/不可能实现的功能。
-
rustyx,如果你所说的对于困难的情况(不是不可能的情况)确实是真的,那么我想是时候从 C++ 继续前进了!
标签: c++ function templates virtual