关于静态多态性的概念我有些疑问,我有些时候会听到; 您可能主要在C++的上下文中解释它们,但我会欣赏适用的语言无关的答案(因此标记C++和语言不可知).
我们如何定义静态多态?作为一个例子,我相信,std::sort因为它取决于由一些对象,其提供了一些接口上从C++函数应被视为静态多态性表现得像迭代器,并且提供迭代器的接口下确切行为可在编译时被确定.这是解释我们如何定义静态多态,或者它只是对特定案例的描述,还有更多内容吗?
在C++中使用静态多态的常见代码模式是什么?另外:SP 只能通过C++中的模板实现吗?
这是真的,一个给定的UML类图不直接描述多态性是如何处理的,因此,它可以被至少部分地或者静态地或者动态地执行?换句话说:静态与动态多态的选择是否独立于OOP模型,因此由实现者决定?
静态多态只是C++特定的并且与模板的工作方式有关吗?如果没有,它是否存在于C++以外的任何其他主流语言中?我们可以在Java,C#中拥有等效的静态多态性吗?任何东西,它会带来什么好处吗?
最重要的...... 使用静态多态性的实际好处是什么?我认为我们可以同意它降低了代码的灵活性; 有什么优点,除了 - 在C++的情况下 - 保存一个指针解除引用(虚函数/指针到函数/委托成本)?什么是静态多态性特别有用的问题类,是实现的正确选择?
我的一个朋友问我"如何使用CRTP替换多级继承中的多态".更准确地说,在这种情况下:
struct A {
void bar() {
// do something and then call foo (possibly) in the derived class:
foo();
}
// possibly non pure virtual
virtual void foo() const = 0;
}
struct B : A {
void foo() const override { /* do something */ }
}
struct C : B {
// possibly absent to not override B::foo().
void foo() const final { /* do something else */ }
}
Run Code Online (Sandbox Code Playgroud)
我和我的朋友都知道CRTP不是多态的替代品,但我们对可以使用这两种模式的情况感兴趣.(为了这个问题,我们对每种模式的利弊都不感兴趣.)
之前已经问过这个问题,但事实证明作者想要实现命名参数idiom …
我知道动态/静态多态性取决于应用程序的设计和要求.但是,如果可能的话,是否建议选择动态静态多态?特别是,我可以在我的应用程序中看到以下2个设计选择,这两个设计似乎都被建议不要:
使用CRTP实现静态多态性:没有vtable查找开销,同时仍以模板基类的形式提供接口.但是,使用很多开关和static_cast来访问正确的类/方法,这是危险的
动态多态性:实现接口(纯虚拟类),将查询成本与偶然的简单函数(如访问器/更改器)相关联
我的应用程序非常关键,所以我赞成静态多态.但需要知道使用过多的static_cast是否表明设计不佳,以及如何在不产生延迟的情况下避免使用.
编辑:感谢您的见解.以具体案例为例,哪一种更好?
class IMessage_Type_1
{
virtual long getQuantity() =0;
...
}
class Message_Type_1_Impl: public IMessage_Type_1
{
long getQuantity() { return _qty;}
...
}
Run Code Online (Sandbox Code Playgroud)
要么
template <class T>
class TMessage_Type_1
{
long getQuantity() { return static_cast<T*>(this)->getQuantity(); }
...
}
class Message_Type_1_Impl: public TMessage_Type_1<Message_Type_1_Impl>
{
long getQuantity() { return _qty; }
...
}
Run Code Online (Sandbox Code Playgroud)
请注意,每个类中都有几个mutators/accessors,我需要在我的应用程序中指定一个接口.在静态多态性中,我只切换一次 - 获取消息类型.但是,在动态多态性中,我使用虚函数进行EACH方法调用.这不是一个使用静态聚合物的情况吗?我相信CRTP中的static_cast非常安全且没有性能损失(编译时限)?
我正在玩静态多态,我正在调用一个函数,它根据初始参数的类型在内部调用"正确"的专用函数(基本上我正在做标记).这是代码:
#include <iostream>
using namespace std;
// tags
struct tag1{};
struct tag2{};
// the compliant types, all should typedef tag_type
struct my_type1
{
using tag_type = tag1;
};
struct my_type2
{
using tag_type = tag2;
};
// static dispatch via tagging
template <typename T>
void f(T)
{
cout << "In void f<typename T>(T)" << endl;
// why can I call f_helper without forward definition?!?
f_helper(typename T::tag_type{});
}
int main()
{
my_type1 type1;
my_type2 type2;
// how does f below knows …Run Code Online (Sandbox Code Playgroud) 这是C++中的一个简单代码:
#include <iostream>
#include <typeinfo>
template<typename T>
void function()
{
std::cout << typeid(T).name() << std::endl;
}
int main()
{
function<int>();
function<double>();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我已经读过C++中的模板是一个编译时功能,它不像C#/ Java中的泛型.
据我所知,C++编译器会将单个定义的函数划分为不同的数字(取决于具有不同类型的调用计数)的函数.
我是对还是不对?我不是C++编译器的专家,所以我向你提出一些建议.
如果我对编译器输出的建议是正确的,我想知道我是否可以将上面的代码描述为静态多态?
因为它似乎没有覆盖,只是从可执行文件中调用副本或者......应用程序在输出二进制映像中的含义并不重要,但只有重要的部分是在C++代码级别而且我不看编译器如何产生输出.
例如,我有一些功能模板
template <typename T>
void foo(T);
template <typename T>
void bar(T);
// others
Run Code Online (Sandbox Code Playgroud)
我需要将每一个传递给一个算法,该算法将用各种类型调用它,例如
template <typename F>
void some_algorithm(F f)
{
// call f with argument of type int
// call f with argument of type SomeClass
// etc.
}
Run Code Online (Sandbox Code Playgroud)
我无法传递我的函数模板未实例化,但我无法使用任何特定类型实例化它,因为some_algorithm需要使用几种不同类型的参数调用它.
我可以将我的函数模板调整为多态函数对象,例如
struct foo_polymorphic
{
template <typename T>
void operator()(T t)
{
foo(t);
}
};
Run Code Online (Sandbox Code Playgroud)
然后传递给它some_algorithm(foo_polymorphic()).但这需要为我的每个功能模板编写一个单独的适配器.
是否有一个通用的适应函数模板是一个多态函数对象,即一些机制,我可以重新使用的每一个我需要适应的功能模板,而不必为每一个单独声明什么方式?
我正在尝试使用CRTP实现编译时多态,并希望强制派生类实现该函数.
目前的实施是这样的.
template <class Derived>
struct base {
void f() {
static_cast<Derived*>(this)->f();
}
};
struct derived : base<derived>
{
void f() {
...
}
};
Run Code Online (Sandbox Code Playgroud)
在此实现中,如果派生类未实现,则对函数的调用将进入无限循环f().
如何强制派生类实现像纯虚函数一样的函数?我尝试使用'static_assert',static_assert(&base::f != &Derived::f, "...")但它会生成一条错误消息,指出指向不同类的成员函数的两个成员函数指针不具有可比性.
我试图使用奇怪的重复模板模式实现静态多态性,当我注意到static_cast<>,通常在编译时检查一个类型是否实际可以转换为另一个,在基类声明中错过了一个错误,允许代码向下转换基础与其中一个兄弟姐妹同课:
#include <iostream>
using namespace std;
template< typename T >
struct CRTP
{
void do_it( )
{
static_cast< T& >( *this ).execute( );
}
};
struct A : CRTP< A >
{
void execute( )
{
cout << "A" << endl;
}
};
struct B : CRTP< B >
{
void execute( )
{
cout << "B" << endl;
}
};
struct C : CRTP< A > // it should be CRTP< C >, but typo …Run Code Online (Sandbox Code Playgroud) 我希望在编译时选择具有多个可能实现的接口.我看到CRTP是实现它的首选成语.这是为什么?另一种选择是策略模式,但我在任何地方都没有提到这种技术:
template <class Impl>
class StrategyInterface
{
public:
void Interface() { impl.Implementation(); }
void BrokenInterface() { impl.BrokenImplementation(); }
private:
Impl impl;
};
class StrategyImplementation
{
public:
void Implementation() {}
};
template <class Impl>
class CrtpInterface
{
public:
void Interface() { static_cast<Impl*>(this)->Implementation(); }
void BrokenInterface() { static_cast<Impl*>(this)->BrokenImplementation(); }
};
class CrtpImplementation : public CrtpInterface<CrtpImplementation>
{
public:
void Implementation() {}
};
StrategyInterface<StrategyImplementation> str;
CrtpImplementation crtp;
Run Code Online (Sandbox Code Playgroud)
BrokenInterface遗憾的是,在任何一种情况下,编译器都没有捕获它,除非我真的尝试使用它.策略变体对我来说似乎更好,因为它避免了丑陋static_cast,它使用组合而不是继承.还有什么CRTP允许的,那个策略没有?为什么主要使用CRTP?
对于模板参数给出的类型,C++有一些鸭子类型.我们不知道什么类型DUCK1和DUCK2将来,但只要他们可以quack(),它将编译和运行:
template <class DUCK1, class DUCK2>
void let_them_quack(DUCK1* donald, DUCK2* daisy){
donald->quack();
daisy->quack();
}
Run Code Online (Sandbox Code Playgroud)
但是写起来有点不方便.当我完全不关心什么实际类型DUCK1和DUCK2有,而是要充分利用鸭打字的想法,那么我想有一些sligthly不同于上面:
C++是否提供任何功能来实现3个想法中的一个或多个?
(我知道虚拟继承在大多数情况下是实现这种模式的首选方法,但这里的问题是关于静态多态的情况.)
c++ ×10
templates ×4
c++11 ×2
crtp ×2
inheritance ×2
duck-typing ×1
optimization ×1
overriding ×1
polymorphism ×1
runtime ×1
static-cast ×1