Nic*_*kFP 0 c++ polymorphism templates c++-concepts
我是模板编程的新手,并且偶然发现了这个习惯用法。例如,类似:
class FooBase {
public:
virtual void do_something() = 0;
};
template <class Foo> // Foo is derived from FooBase
void g(Foo& foo) {
static_assert(std::is_base_of<FooBase, Foo>::value);
// ...
foo.do_something();
// ...
}
Run Code Online (Sandbox Code Playgroud)
在我看来,这种模式很有用,因为:
requires子句(如果可用的话)相比,指定属性、参数和返回类型很容易。FooBase标头即可清楚定义新 Foo 类的要求。FooBase类中。但是我担心使用的性能影响virtual函数对性能的影响。我的理解是运行时没有成本,因为该函数是从派生类调用的;但链接器将无法内联该函数。
另一个缺点是不允许虚拟模板功能。
最后,我担心这可能是一种代码味道,因为它使用运行时多态性功能来执行静态检查。C++20 概念可能是执行此操作的“正确”方法,但由于上述三个原因,它们似乎不太方便。
这是不好的做法,因为您使用了错误的工具来完成这项工作。您编写的代码传达了错误的信息,并且没有实际的执行机制。
动态多态性的要点在于它是动态的:在运行时定义,以便您可以将对象传递给不完全知道在编译时给出的类型的函数。这允许在一个位置针对基类编写代码,而不是导出到标头等中。并且任何时候使用的实际类也不能导出到标头等中。只有类的来源需要知道实际类型。
像模板这样的编译时多态性是基于在编译时了解一切的。所有代码,无论是源代码还是目标代码,都需要知道类型是什么。
从本质上讲,与编译时多态性相比,您喜欢动态多态性阐明其要求的方式,但您尝试通过使用编译时多态性来回避其性能成本。这会造成代码混乱,因为您混淆了机制。
如果有人看到带有函数的基类virtual,他们会假设您的代码将传递指向实际类的指针/引用。这是在大多数情况下使用它们的期望的一部分(甚至virtual基于类型的擦除有效地做到了这一点)。相反,看到一堆按值获取类型的模板函数会令人困惑。
此外,您没有执行机制。采用 a 的函数可FooBase&确保用户无法使用不是实际FooBase派生类型的类型来调用它(好吧,您可以将其隐式转换为类型,但让我们在这里忽略背信弃义)。您的模板函数避开了概念,没有类似的强制执行。您可以记录它必须是FooBase派生类型,但您不能静态强制它。
至少,模板参数应该声明为std::derived_from<FooBase> Foo(并且不,static_assert这不是一个好主意)。
但实际上,您应该使用正确的概念。您想要编译时多态性,无论您个人对概念的感受如何,概念都是C++20 用于定义编译时多态性原型的语言机制。如果您使用概念,没有人会对代码的含义和意图感到困惑。