首页 > 解决方案 > 有没有办法使用 SFINAE 来检测一个类型是否实现了给定的抽象基类?

问题描述

这是这个问题的简单用例。(注意:我意识到此处显示的代码模式可能不被视为最佳实践;示例代码仅用于说明手头的主题)

目前,我有这个方便的小模板函数,它将从堆中分配任意给定对象的副本,并返回该副本:

template<typename T> inline T * CloneStaticObject(const T & item) 
{
   return new T(item);
}

适用于具有复制构造函数的任何类型......直到我传入一个对基类的引用类型,然后我被对象切片问题所困扰。

所以为了处理这个问题,我还有一个很好的 OOP 风格的克隆接口,我可以使用它,它带有一个CloneDynamicObject(const ICloneable &)可以正确克隆任何继承自的对象的函数ICloneable,无论传入引用的类型是什么:

class ICloneable
{
public:
   virtual ~ICloneable() {/* empty */}

   virtual ICloneable Clone() const = 0;
};

class MyBaseClass : public ICloneable
{
public:
   MyBaseClass() {/* empty */}

   virtual ICloneable * Clone() const {return new MyBaseClass(*this);}      
};

class MySubClass : public MyBaseClass
{
public:
   MySubClass() {/* empty */}

   virtual ICloneable * Clone() const {return new MySubClass(*this);}      
};

template<typename T> inline T * CloneDynamicObject(const T & item) 
{
   return static_cast<T *>(item.Clone());
}

...这也很好用,只要我小心地只传递一个作为 ICloneable 对象引用的参数。

但是现在优雅已经开始了,我想创建一个CloneAnyObject(const T & item)无论如何都会做正确事情的函数,例如:

 // This doesn't work but it shows the idea
 template<typeName T> inline T * CloneAnyObject(const T & item)
 {
    const ICloneable * cloneMe = dynamic_cast<const ICloneable *>(&item);
    if (cloneMe) return CloneDynamicObject(*cloneMe)
            else return CloneStaticObject(item);
 }

...上面的实现几乎可以满足我的要求,除了它有两个问题:

  1. dynamic_cast<>不适用于所有类型
  2. 即使这样做了,dynamic_cast()也要在运行时测试对象,如果可能的话,我想通过在编译时评估 test-for-ICloneability 来避免这种开销。

我的问题是,有什么方法可以使用 SFINAECloneAnyObject()正确实现该功能?

(请注意,我知道用于测试类型名是否具有给定名称的方法的 SFINAE 技巧,我认为这是一种非常有用的技术,但这并不是我在这里寻找的;我在寻找什么for 相反是一种测试类型是否从接口继承的方法)

标签: c++interfacesfinae

解决方案


您可以使用std::is_base_of特征静态确定一个类型是否派生自另一个类型。可以按如下方式使用它来将您的函数拆分为一个用于派生类型的版本和一个用于其他类型的版本:

// For derived types
template <typename T, std::enable_if_t<std::is_base_of_v<ICloneable, T>, int> = 0>
inline T * CloneAnyObject(const T & item) {
    return CloneDynamicObject(item);
}

// For non-derived types (note the !)
template <typename T, std::enable_if_t<!std::is_base_of_v<ICloneable, T>, int> = 0>
inline T * CloneAnyObject(const T & item) {
    return CloneStaticObject(item);
}

如果您使用的是 C++17,您还可以使用 Jarod42 的答案来简化if constexpr此操作


推荐阅读