使用可变参数模板限制受支持的模板类类型

时间:2014-08-19 17:42:31

标签: c++ templates c++11 variadic-templates

我试图处理只与以下某些组合兼容的图像处理操作类:

  • 一组维度[1,2,3,...]
  • 一组类型[int,float,double,...]

一种工作方法是定义一个通用模板类来处理默认情况(即:什么都不做),如下所示:

template <int dimension, typename dataType>
class IPOperation
{
public:
    void execute()
    {
        std::cout << "do nothing" << std::endl;
    }
};

然后我必须为所有支持的类型组合编写该类的特化,例如:

template<>
class IPOperation<2,float>
{
public:
    void execute()
    {
        std::cout << "do something" << std::endl;
    }
};

但是由于支持的维度/类型组合的数量可能变得非常大,这种方法总是有点过分。

受策略驱动设计的启发,为模板类配备特定限制会更好,如下所示:

template <int dimension, typename dataType>
class IPOperation : public supportedDimensions<2,3,4>, public supportedTypes<int, float, double>
{
    void execute()
    {
        std::cout << "execute()" << std::endl;
    }
};

到目前为止,我的糟糕做法是这样(而且看起来非常糟糕,我无法帮助自己):

#include <iostream>
#include <vector>

template <int... supportedDimensions>
class IPDimensions
{
private:
public:
    static std::vector<int> getSupportedDimensions()
    {
        return { supportedDimensions... };
    }

    static bool supportsDimension(int dim)
    {
        std::vector<int> dimensions = getSupportedDimensions();
        return std::find(dimensions.begin(), dimensions.end(), dim) != dimensions.end();
    }
};

template <int dim>
class IPOperation : public IPDimensions<2,3>
{
private:
public:
    void execute(void)
    {
        if(IPOperation::supportsDimension(dim))
        {
            std::cout << dim << "d is supported -> execute" << std::endl;
        }
        else
        {
            std::cout << dim << "d is not supported -> sit down and do nothing" << std::endl;
        }
    }
};

int main(int argc, const char * argv[])
{
    IPOperation<2>* okay = new IPOperation<2>();
    IPOperation<4>* notOkay = new IPOperation<4>();

    okay->execute();
    notOkay->execute();
}

当尝试将类似的东西应用于类型时,我完全迷失了。如果您使用策略作为特定策略的委托,那么使用某种机制来进行某些检查会很不错。也许我的方法是错误的方式,这整个事情可以通过宏,枚举或特征和std :: enable_if更简单地实现,使函数仅对定义的场景可见,但因为我花了一些时间阅读一些c ++ 11主题,我真的不确定任何事情了。

先谢谢你们提出任何有用的建议!

3 个答案:

答案 0 :(得分:1)

我使用SFINAE来解决这类问题,例如:

template<std::size_t dimensions, typename dataType, typename=void> class IPOperation;

template<std::size_t dimensions, typename dataType>
class IPOperation<dimensions, dataType,
                  std::enable_if<(0<dimensions && dimensions<4) && 
                                 std::is_floatingpoint<dataType>::value >::type>
{
   /* ... */
};

IPOperation<2,float> a; // okay
IPOperation<4,float> b; // compile-time error: wrong dimensionality
IPOperation<3,int>   c; // compile-time error: wrong dataType

static_assert

template<std::size_t dimensions, typename dataType>
class IPOperation<dimensions, dataType>
{
   static_assert(0<dimensions && dimensions<4,"wrong dimension");
   static_assert(std::is_floatingpoint<dataType>::value,"incompatible data type");
   /* ... */
};

它提供了更好的错误消息。

答案 1 :(得分:1)

如果要为不支持的类型允许虚拟操作(但没有编译错误),可以使用类似下面的内容:

#include <type_traits>
#include <iostream>

template <class...>
struct supportedTypes {
    template <class X>
    static constexpr bool check() { return false; };
};
template <class A, class... R>
struct supportedTypes<A, R...> {
    template <class X>
    static constexpr bool check() {
        return std::is_same<X, A>::value
        || supportedTypes<R...>::template check<X>(); }
};

int main() {
    std::cout << supportedTypes<int,double>::check<int>();
    std::cout << supportedTypes<int,double>::check<void>();
}

答案 2 :(得分:0)

您可以利用boost::mpl::set检查是否支持给定的维度/类型。如果要在编译时拒绝不支持的类型,请使用static_assert;如果您确实需要“默认情况下不执行任何操作”逻辑,请使用SFINAE。

static_assert

#include <iostream>

#include <boost/mpl/set.hpp>
#include <boost/mpl/set_c.hpp>

using namespace boost::mpl;

template <std::size_t Dim, typename Type>
struct Op {

  static_assert(has_key<set_c<std::size_t, 2, 3, 4>,
                        integral_c<std::size_t, Dim>>::value,
                "Unsupported dimension!");
  static_assert(has_key<set<int, float, double>, Type>::value,
                "Unsupported type!");

  void Execute() {
    std::cout << "DoSomething" << std::endl;
  }

};

int main() {
  // Op<1, int> x;  // error: Unsupported dimension!
  // Op<2, std::string> x;  // error: Unsupported type!
  Op<2, int> x;
  x.Execute();
}

打印:

DoSomething

SFINAE

#include <iostream>

#include <boost/mpl/set.hpp>
#include <boost/mpl/set_c.hpp>

using namespace boost::mpl;

template <std::size_t Dim, typename Type, typename = void>
struct Op {

  void Execute() {
    std::cout << "DoNothing" << std::endl;
  }

};

template <std::size_t Dim, typename Type>
struct Op<Dim,
          Type,
          std::enable_if_t<has_key<set_c<std::size_t, 2, 3, 4>,
                                   integral_c<std::size_t, Dim>>::value &&
                           has_key<set<int, float, double>, Type>::value>> {

  void Execute() {
    std::cout << "DoSomething" << std::endl;
  }

};

int main() {
  Op<1, int> x;
  Op<2, int> y;
  x.Execute();
  y.Execute();
}

打印:

DoNothing
DoSomething