1

我有一个通用Matrix类。当我处理值矩阵时,我有 SSE 优化的矩阵乘法函数Float。目前,我的方法包括一个名为“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

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

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

矩阵.h

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

矩阵.cpp

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

only的函数doSSE_MulMM对浮点矩阵真正有意义,但我更喜欢将其设为成员函数,因为它对矩阵的私有数据成员进行操作。有没有一种好方法可以将函数专门化为只存在于 Matrix 类的一个专门化中?我想我可以介绍一个通用版本,它会为其他数据类型引发异常,但这看起来很混乱。

4

4 回答 4

3

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

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

矩阵.hpp

#include <iostream>

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

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

矩阵.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。

活生生的例子

于 2013-08-31T00:32:08.147 回答
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
}
于 2013-08-30T14:28:08.577 回答
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'
}
于 2013-08-30T15:03:18.860 回答
0

为此,您需要专门化完整的模板矩阵。您不能只专门化类模板的一个成员函数。然后,您可以根据需要添加私人成员。如果您在专业化中小心履行模板合同,Matrix 上的通用算法将不会知道其中的区别。

于 2013-08-30T13:35:44.297 回答