如何根据类型是整型还是浮点型来更改模板方法?

3
我正在开发一个矩阵类,可以接受整数类型(short、int、long)和浮点数类型(float、double)。
我希望某些方法只适用于浮点数类型(例如倒数方法),而某些方法对浮点数类型和整数类型有不同的实现(例如等于运算符)。
我有一种感觉,正确的方法是使用boost的“enable_if”和“is_integral”/“is_floating_point”,但我似乎无法让它起作用。
我的实现类似于以下C++半伪代码:
template <typename T>
class Matrix
{
    ...
    bool operator==(Matrix<typename enable_if<is_integral<T> T >::type >) const;
    bool operator==(Matrix<typename enable_if<is_floating_point<T>::type T> >) const;
    typename enable_if<is_floating_point<T> T> computeInverse() const;
    ...
};
// implementation
bool Matrix<T>::operator==(Matrix<typename enable_if<is_integral<T> T >::type >) const {
  //implementation without precision
}
bool Matrix<T>::operator==(Matrix<typename enable_if<is_integral<T> T >::type >) const {
  //implementation using precision
}
Matrix<typename enable_if<is_floating_point<T> T>::type > Matrix<T>::computeInverse() const {
  //implementation requiring floating points
}

这会导致很多编译错误,我认为以下是最相关的错误信息:
error: no type named ‘type’ in ‘struct boost::enable_if<boost::is_integral<float>, float>’

并且。
error: no type named ‘type’ in ‘struct boost::enable_if<boost::is_floating_point<int>, int>’

这表示我不能使用boost的enable_if来为不同的类型提供不同的实现,这是正确的吗?
如果是这样,我该怎么办? 我知道模板特化是一种方法,但我想避免重复太多的代码。

3
我会尝试思考是否可以将矩阵分解为公共部分(移动到基本类型)和两个针对不同类型的整个矩阵类的特殊化,而不是将实现混合在单个模板中。 - David Rodríguez - dribeas
2个回答

2
最简单的方法是在Matrix中使用重载函数:
template <typename T>
class Matrix
{
    template <bool isInteger> class Discrim;
    //  ...
    bool isEqual( Matrix const& other, Discrim<true> ) const
    {
        //  Integer implementation...
    }

    bool isEqual( Matrix const& other, Discrim<false> ) const
    {
        //  Floating point implementation...
    }

public:
    bool isEqual( Matrix const& other ) const
    {
        return isEqual( other, Discrim<std::numeric_limits<T>::is_integer>() );
    }
};

你的operator==operator!=会调用Matrix::isEqual函数。
话虽如此,从你的评论来看,如果T是浮点类型,你想要一个“几乎相等”的函数。不要这样做。这只会让人们感到困惑,并在以后引起无尽的问题(因为==将不再是可传递的操作)。

谢谢你的回答和提醒关于“几乎相等”的功能。当我找到当前问题的解决方案后,我会寻找另一种解决方案。 - jpihl

1

你应该尝试遵循@DavidRodriguez的建议,将类的通用功能分离到一个基类中;然后提供整个派生类的特殊化,其中功能不同。这将是更好的方法。

如果您确实想保留当前的实现,可以使用SFINAE从重载决议中排除不需要的运算符版本,如下所示:

#include <iostream>
#include <type_traits>

template<class T>
struct Matrix
{
  // ...
};

template<class T>
typename std::enable_if<
    std::is_integral<T>::value, bool
  >::type
operator==( const Matrix<T>&, const Matrix<T>& )
{
  std::cout << "Integer version" << std::endl;
  return true;
}

template<class T>
typename std::enable_if<
    !std::is_integral<T>::value, bool
  >::type
operator==( const Matrix<T>&, const Matrix<T>& )
{
  std::cout << "Floating point version" << std::endl;
  return true;
}

int main()
{
  Matrix<int> m1, m2;
  Matrix<double> m3, m4;

  if( m1 == m2 ) {}

  if( m3 == m4 ) {}
}

如果你想将运算符作为成员函数,我所知道的唯一方法是在运算符中添加一个虚拟模板参数,并在std::enable_if测试条件中添加&& std::is_same。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接