静态多态对实现接口有意义吗?
祝大家圣诞快乐!
我正在学习静态多态性,并且正在阅读 Andrei Alexandrescu 关于基于策略的设计的优秀书籍.我在我的代码中遇到了以下内容:我有接口 Interface
指定方法 Foo
必须存在.该接口将由类 Impl
实现.我有以下两种选择:
I am learning about static polymorphism and I'm reading Andrei Alexandrescu's excellent book on policy-based design. I came across the following, in my code: I have interface Interface
which specifies that method Foo
must be present. This interface will be implemented by class Impl
. I have the following two options:
1) 动态多态性
class Interface {
public:
virtual void Foo() = 0;
}
class Impl : public Interface {
public:
void Foo() {};
}
2) 静态多态
class Impl {
{
public:
void Foo() {};
}
template <class I>
class Interface : public I
{
public:
void Foo() { I::Foo(); } //not actually needed
}
在这种情况下使用静态多态有意义吗?与第一种方法相比,第二种方法有什么好处吗?接口只指定了一些方法的存在,并且它的机制对于不同的实现是相同的――所以不太像书中描述的案例,所以我觉得我可能只是过于复杂了.
Does it make sense to use static polymorphism in this case? Does the second approach offer any benefits compared to the first one? The interface specifies only the presence of some methods, and its mechanics are the same for different implementation - so not quite like the cases described in the book, so I feel I may only be over-complicating things.
更新:我在运行时不需要多态行为;正确的实现在编译时就知道了.
Update: I do not need polymorphic behaviour at run-time; the correct implementation is known at compile-time.
推荐答案
检查接口.
动态多态确实会强制孩子尊重接口.
Checking Interface.
Dynamic polymorphism does force the child to respect the interface.
静态多态性不会强制孩子尊重接口(直到你真正调用该函数),所以,如果你不提供有用的方法,你可以直接使用Impl
.
Static polymorphism does NOT force the child to respect the interface
(until you really call the function), So, if you don't provide useful method,
you may use directly Impl
.
class InvalidImpl {}; // Doesn't respect interface.
void bar()
{
InvalidImpl invalid;
// this compiles, as not "expected" since InvalidImpl doesn't respect Interface.
CRTP_Interface<InvalidImpl> crtp_invalid;
#if 0 // Any lines of following compile as expected.
invalid.Foo();
crtp_invalid.Foo();
#endif
}
您有第三种方法使用特征来检查一个类是否验证了一个接口:
You have a 3rd way using traits to check that a class verify an Interface:
#include <cstdint>
#include <type_traits>
// Helper macro to create traits class to know if class has a member method
#define HAS_MEM_FUNC(name, Prototype, func)
template<typename U>
struct name {
typedef std::uint8_t yes;
typedef std::uint16_t no;
template <typename T, T> struct type_check;
template <typename T = U>
static yes &chk(type_check<Prototype, &T::func> *);
template <typename > static no &chk(...);
static constexpr bool value = sizeof(chk<U>(0)) == sizeof(yes);
}
// Create traits has_Foo.
HAS_MEM_FUNC(has_Foo, void (T::*)(), Foo);
// Aggregate all requirements for Interface
template <typename T>
struct check_Interface :
std::integral_constant<bool, has_Foo<T>::value /* && has_otherMethod<T>::value */>
{};
// Helper macros to assert if class does respect interface or not.
#define CHECK_INTERFACE(T) static_assert(check_Interface<T>::value, #T " doesn't respect the interface")
#define CHECK_NOT_INTERFACE(T) static_assert(!check_Interface<T>::value, #T " does respect the interface")
使用 C++20 概念,可以以不同的方式编写特征:
With C++20 concepts, traits can be written differently:
// Aggregate all requirements for Interface
template <typename T>
concept InterfaceConcept = requires(T t)
{
t.foo();
// ...
};
#define CHECK_INTERFACE(T) static_assert(InterfaceConcept<T>, #T " doesn't respect the interface")
让我们测试一下:
class Interface {
public:
virtual void Foo() = 0;
};
class Child_Impl final : public Interface {
public:
void Foo() override {};
};
#if 0 // Following doesn't compile as expected.
class Child_InvalidImpl final : public Interface {};
#endif
template <class I>
class CRTP_Interface : public I
{
public:
void Foo() { I::Foo(); } // not actually needed
};
class Impl { public: void Foo(); }; // Do respect interface.
class InvalidImpl {}; // Doesn't respect interface.
CHECK_INTERFACE(Interface);
CHECK_INTERFACE(Child_Impl);
CHECK_INTERFACE(Impl);
CHECK_INTERFACE(CRTP_Interface<Impl>);
CHECK_NOT_INTERFACE(InvalidImpl);
CHECK_INTERFACE(CRTP_Interface<InvalidImpl>); // CRTP_Interface<T> _HAS_ Foo (which cannot be invoked)
性能
使用动态多态,您可以为虚拟调用付费.您可以通过将 final
添加为 class Child final : public Interface
来减少一些虚拟调用.
Performance
With Dynamic Polymorphism, you may pay for virtual call. You may reduce some virtual call by adding final
as class Child final : public Interface
.
所以编译器可能会优化如下代码:
So compiler may optimize code like:
void bar(Child& child) { child.Foo(); } // may call Child::Foo not virtually.
但它不能做任何魔术(假设 bar
没有内联):
but it can't do any magic (assuming bar
not inlined) with:
void bar(Interface& child) { child.Foo(); } // have to virtual call Foo.
现在,假设您的界面中有:
Now, assume that in your interface you have:
void Interface::Bar() { /* some code */ Foo(); }
我们处于第二种情况,我们必须虚拟调用 Foo
.
we are in the second case where we have to virtual call Foo
.
静态多态通过以下方式解决了这个问题:
Static polymorphism solves that by:
template<class Derived>
void Interface<Derived>::Bar() { /* some code */ static_cast<Derived*>(this)->Foo(); }
相关文章