如何创建仅适用于特定模板特化的函数

时间:2013-08-30 13:27:42

标签: c++ templates template-specialization

我有一个通用的Matrix类。当我处理Float值的矩阵时,我有SSE优化的矩阵乘法函数。目前,我的方法包括一个名为“doSSE_mulMM”的函数,它通过矩阵乘法来执行矩阵,包括几个检查,但这只与Matrix<Float>相关(它部分存在,因为我在代码中检查了SSE功能如果没有SSE,则转向效率较低的乘法。)

对于我们的构建服务器正在运行的GCC版本,我收到此错误:

  

error: specialization of ‘MTI::Matrix<float>& MTI::Matrix<BT>::doSSE_MulMM(const MTI::Matrix<float>&, const MTI::Matrix<float>&, bool) [with BT = float]’ after instantiation

相同的代码在我们的Linux主机上的Visual Studio和旧版GCC中编译良好。

我无法提供完整的代码,但这些是函数的签名:

Matrix.h

template <class BT> 
class Matrix {
    ....
    Matrix<Float>&  doSSE_MulMM      (const Matrix<Float>& mat1, const Matrix<Float>& mat2, bool softmax);
    ....
}

Matrix.cpp

template <> 
Matrix<Float>&  Matrix<Float>::doSSE_MulMM (const Matrix<Float>& mat1, 
                                 const Matrix<Float>& mat2,
                                 bool softmax) {
    ....
}

doSSE_MulMM的功能只对Float矩阵有意义,但我更倾向于将其作为成员函数,因为它对Matrix的私有数据成员进行操作。有没有一种很好的方法可以将函数专门化,只存在于Matrix类的一个特化中?我想我可以引入一个通用版本来引发其他数据类型的异常,但这看起来很混乱。

4 个答案:

答案 0 :(得分:3)

它似乎不是一个广为人知的功能,但你可以专门化类模板的非模板成员函数(这就是你试图做的)。

正如我在评论中所写,你只需要告诉编译器在另一个TU 中有这样的特殊化,这样它就不会尝试从模板中实例化该函数。

Matrix.hpp

#include <iostream>

template < typename T >
struct Matrix
{
    void multiply()
    {
        std::cout << "non-specialized" << std::endl;
    }
};

template <>
void Matrix<float>::multiply();

Matrix.cpp

#include <iostream>
#include "Matrix.hpp"

template <>
void Matrix<float>::multiply();
{
    std::cout << "specialized for float" << std::endl;
}

some_other.cpp

#include "Matrix.hpp"

int main()
{
    Matrix<int>{}.multiply();
    Matrix<float>{}.multiply();
}

标准中的相关段落可能是[temp.inst] / 2:

  

除非已明确实例化类模板或成员模板的成员或显式专用,否则在需要成员定义的上下文中引用特化时,将隐式实例化成员的特化。存在; [...]

也就是说,您需要声明template <> void Matrix<float>::multiply();来阻止实例化。如果防止实例化,那么除了显式特化之外没有void Matrix<float>::multiply()的定义,因此不会违反ODR。

Live example

答案 1 :(得分:1)

只有一种类型存在功能的简单方法是CRTP,并在那里专门化。 CRTP使您可以在实现该功能的位置访问完整类型,并且专门化使您可以仅为某些类型存在该功能。

这是一个玩具示例:

template<typename D, typename T>
struct foo_for_float {};
template<typename D>
struct foo_for_float<D, float> {
  D* self() {
    static_assert( std::is_base< foo_for_float<D, float>, D >::value, "CRTP error" );
    return static_cast<D*>(this);
  }
  D const* self() const {
    static_assert( std::is_base< foo_for_float<D, float>, D >::value, "CRTP error" );
    return static_cast<D const*>(this);
  }
  void foo() { // const if you want to
    // use self() in this method instead of this
  }
};
// The usual CRTP magic "pass my own type to my parent":
template<typename T>
struct test : foo_for_float<test<T>, T> {
  void bar() {}
}
int main() {
  test<int> a;
  test<float> b;
  a.bar(); // valid
  b.bar(); // valid
  b.foo(); // valid
  a.foo(); // not found
}

答案 2 :(得分:1)

这是另一种基于SFINAE的方法:

// matrix.h
#include <type_traits>
#include <iostream>

template <typename T>
struct matrix {

    // This is active only for U == T == float.
    // Otherwise, it does not participate in overload resolution.
    // Essentially this declaration is equivalent to:
    // matrix<float>& multiply(const matrix<float>& other);
    template <typename U>
    typename std::enable_if<
        std::is_same<U, T>::value &&
        std::is_same<U, float>::value,
        matrix&
    >::type
    multiply(const matrix<U>& other); // no definition here (convenient but not required)

    // This is active only for U == T != float
    // Otherwise, it does not participate in overload resolution.
    // Essentially this declaration is equivalent to:
    // matrix<T>& multiply(const matrix<T>& other); // for T != float
    template <typename U>
    typename std::enable_if<
        std::is_same<U, T>::value &&
        !std::is_same<U, float>::value,
        matrix&
    >::type
    multiply(const matrix<U>&) {
        std::cout << "generic multiplication\n";
        return *this;
    }

};

然后

// matrix.cpp
#include "matrix.h"

// The definition of
// matrix<float>& multiply(const matrix<float>& other);
template <>
template <>
matrix<float>&
matrix<float>::multiply<float>(const matrix<float>& other) {
    std::cout << "specific multiplication\n";
    return *this;
}

最后,在客户端代码中:

#include "matrix.h"

int main() {

    matrix<int> mi1, mi2;
    mi1.multiply(mi2); // outputs 'generic multiplication'

    matrix<float> mf1, mf2;
    mf1.multiply(mf2); // outputs 'specific multiplication'
}

答案 3 :(得分:0)

为此,您需要专门化完整模板Matrix。 您不能只专门化一个类模板的一个成员函数。 然后,您可以根据需要添加私有成员。 如果您在专业化中小心地完成模板合同,Matrix上的通用算法就不会知道差异。