排序独立的可变参数模板参数

问题描述

我想创建一个“映射器”系统,即采用输入(模板)值并返回输出(模板)值的对象。映射器可以具有其他功能/特征。

示例代码

// a trait
template <typename I,typename O>
class Writable
{
public:
  virtual ~Writable() = default;
  virtual O & operator()(I i) = 0;
};

// another trait
template <typename I,typename O>
class Bounded
{
public:
  virtual ~Bounded() = default;
  virtual O min() const = 0;
  virtual O max() const = 0;
};

// base class
template <typename I,typename O,template<typename,typename> class ... Traits>
class IMap
  : public Traits<I,O>...
{
public:
  virtual ~IMap() = default;
  virtual O operator()(I i) const = 0;
};

// a test mapper
class Test1
  : public IMap<int,double,Writable,Bounded>
{
public:
  double operator()(int i) const override { return _d; }
  double & operator()(int i) override { return _d; }
  double min() const override { return 0; }
  double max() const override { return 1; }
private:
  double _d;
};

// another test mapper
class Test2
  : public IMap<int,Bounded,Writable>
{
public:
  double operator()(int i) const override { return _d; }
  double & operator()(int i) override { return _d; }
  double min() const override { return 0; }
  double max() const override { return 1; }
private:
  double _d;
};

// a class to use mappers
template <typename I,typename O>
class MapUse
{
public:
  // I would like this function to accept an IMap containing "at least" the Bounded trait
  // Ideally this function signature should be like
  void method_requiring_writable(IMap<I,O,Bounded> & map)
  {
    map(123)
    map(123) = 0;
  }
  // I would like this function to accept an IMap containing "at least" the Bounded and Writable traits
  // (no matter the actual order of those traits in variadic template parameter )
  // Ideally this function signature should be like
  void method_requiring_bounded_and_writable(IMap<I,Writable> & map)
  {
    map(123);
    map(123) = 0;
    map.min();
  }
};

void test()
{
  Test1 t1;
  Test2 t2;
  Test2 * p = dynamic_cast<Test2*>(&t1); // << always return nullptr,of course !

  MapUse<int,double> mu;
  mu.method_requiring_writable(t1); // << does not compile,not the right type
  mu.method_requiring_bounded_and_writable(t1); // << does not compile,not the right type

  return;
}

第一行错误:mu.method_requiring_writable(t1):

error C2664: 'void MapUse<int,double>::method_requiring_writable(IMap<I,Bounded> &)': cannot convert argument 1 from 'Test1' to 'IMap<I,Bounded> &'
>          with
>          [
>              I=int,>              O=double
>          ]

第一行错误:mu.method_requiring_bounded_and_writable(t1):

error C2664: 'void MapUse<int,double>::method_requiring_bounded_and_writable(IMap<int,Writable> &)': cannot convert argument 1 from 'Test1' to 'IMap<int,Writable> &'

我完全理解为什么这行不通,但是我想让它起作用!

一个问题:可变参数模板参数中的特征顺序不计算在内。可以通过强制开发人员遵守特征顺序来轻松解决此问题,但这并不理想

第二个问题:能够创建需要“至少”具有一组特定特征的映射的方法。对此一无所知。

第一种方法不是可变的,而是-具有非常多的虚拟遗产的-非常复杂的层次结构->地狱般的丑陋,添加新特性真是痛苦!

使用上面的代码,我曾考虑过在IMap中添加某种类型的转换运算符,但没有这样做。

任何C ++ / variadic /魔术专家可以帮助您解决此问题吗?

解决方法

您可以使用类似这样的内容:

#include <type_traits>    

template <template <class,class> class... Traits,std::enable_if_t<
     std::is_base_of<Bounded<I,O>,IMap<I,O,Traits...>>::value
       and std::is_base_of<Writable<I,Traits...>>::value,int> = 0>
void method_requiring_bounded_and_writable(IMap<I,Traits...> & map) {
  // Do stuff
}
,

您为什么不首先检查混合类型?您在这里混合了通用和非通用编程。只需使用已实现的特征(例如min()max()),而无需注意它们是如何实现的。简便易行的 clean 解决方案是继续使用通用编程:

//template <typename I,typename O>
class MapUse {
public:
    template <typename T>
    void method_requiring_writable(T& map) {
        map(123);
        map(123) = 0;
    }

    template <typename T>
    void method_requiring_bounded_and_writable(T& map) {
        map(123);
        map(123) = 0;
        map.min();
    }
};

其他解决方案需要大量的元编程,只会使您的代码更加复杂且难以阅读。我了解这可能无法解决您的特定问题。这是一个建议,可以重新考虑您的设计,并完全跳入或完全放弃通用火车。