【问题标题】:How to limit template parameters to a descendent that is a specialization of a templated interface?如何将模板参数限制为模板化接口的特化的后代?
【发布时间】:2015-09-08 20:17:47
【问题描述】:

假设以下情况:

  1. 有一个模板化接口,定义了对不同数据类型的一组操作。
  2. 此接口由定义实际数据类型操作的各种专用类实现。
  3. 有一些管理类必须处理 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 的模板参数被 DosIt 声明中执行的特化包装和隐藏。

如何将管理类 (NeedsSomeoneWhoCanDoIt) 模板参数“T”限制为 ICanDoIt 的任何 特化,无论在 ICanDoIt 特化期间为 R、S 选择了哪种类型? p>

【问题讨论】:

    标签: c++ templates interface template-specialization typetraits


    【解决方案1】:

    您始终可以使用于实例化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 而无需额外的类型检查。

    【讨论】:

    • @norritt:尽管它对我有用,但您当前的设计仍然感觉像是运行时多态性的奇怪嵌合体和过度限制的概念化身。
    • 这是因为似乎没有办法在单个容器中管理具有不同模板参数的模板类实例,除非使用 Boost (boost::any)。这是我使用 STL 的解决方法的一部分,这意味着只有我自己不太满意,但那是我想出的最好的事情。 C# 风格的运行时类型转换将使所有这些问题消失;)
    • 我对此表示反对,因为如果T 不是 ICanDoIt,那么您将得到的编译错误是no type named 'R_t' in 'struct T',这很漂亮无济于事。 T.C.'s solution 确实让您更清楚地了解静态断言消息。
    • @Barry:是的,TC 的解决方案绝对不那么具有侵入性,而且完全更好,但我相信错误的T 使构建失败已经很有帮助(我可能会在那里添加评论)。
    【解决方案2】:

    您不需要发布模板参数。标准的基于 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));
    

    【讨论】:

      猜你喜欢
      • 2015-02-19
      • 2011-05-10
      • 1970-01-01
      • 2018-06-25
      • 1970-01-01
      • 2017-10-15
      • 1970-01-01
      • 2017-01-20
      相关资源
      最近更新 更多