如何将模板参数限制为模板化接口的专门化的后代

How to limit template parameters to a descendent that is a specialization of a templated interface?

本文关键字:接口 后代 专门化 参数      更新时间:2023-10-16

假设如下情况:

    有一个模板接口,定义了一组不同数据类型的操作。
  1. 该接口由定义实际数据类型操作的各种专用类实现。
  2. 有一些管理类必须工作2中定义的类的实例。

简化后的示例代码如下所示:

#include <iostream>
#include <type_traits>
template <typename R, typename S>
class ICanDoIt
{
    public:
        virtual void doStuff() = 0;
    protected:
        ICanDoIt<R, S>(R rA, S sA) : r(rA), s(sA) {};
        R r;
        S s;
};
class DoesIt : public ICanDoIt<int, double>
{
        public:
            DoesIt(int iA, double dA) : ICanDoIt(iA, dA) {};
            virtual void doStuff()
                { std::cout << "r * s = " << r * s << " done." << std::endl; }
};
template <typename T>
class NeedsSomeoneWhoCanDoIt
{
    static_assert(std::is_base_of<ICanDoIt<R, S>, T>::value, 
                  "T needs to be able to do it.");
    public:
        NeedsSomeoneWhoCanDoIt(const T& doesItA) : doesIt(doesItA) {};
        void getItDone() { doesIt.doStuff(); };
    private:
        T doesIt;
};

int main()
{
    DoesIt doesIt(5, 2.2);
    NeedsSomeoneWhoCanDoIt<DoesIt> needsIt(doesIt);
    needsIt.getItDone();
}

如果你解模板接口"ICanDoIt"代码将实际工作。但是模板化版本的static_assert将失败,因为ICanDoIt的模板参数被封装并隐藏在DoesIt声明中执行的特化中。

如何将管理类(NeedsSomeoneWhoCanDoIt)模板参数"T"限制为 ICanDoIt的任何专门化,而不管在ICanDoIt专门化期间为R, S选择了哪种类型?

您始终可以使用于实例化ICanDoItRS的实际类型对派生类可访问,即

template <typename R, typename S> class ICanDoIt {
public:
  typedef R R_t;
  typedef S S_t;
  virtual void doStuff() = 0;
};

使你的static_assert变成

static_assert(std::is_base_of<ICanDoIt<typename T::R_t, typename T::S_t>,
                              T>::value,
              "T needs to be able to do it.");

根据你的实际代码看起来像设计可能会变得更清晰,如果你定义一个纯抽象的基类(即一个实际类型ICanDoItBase而不是一个模板),你会继承ICanDoIt目前模板化的功能,这将再次成为DoesIt的基础。

NeedsSomeoneWhoCanDoIt可以直接使用多态基类ICanDoItBase,而不需要任何额外的类型检查。

您不需要发布模板参数。标准的基于sfinae的方法可以很好地工作。

namespace detail {
    template<class R, class S>
    std::true_type test(ICanDoIt<R, S>*);
    std::false_type test(...);
}
template<class T>
using can_do_it = decltype(detail::test((T*)nullptr));