我有一个通用的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类的一个特化中?我想我可以引入一个通用版本来引发其他数据类型的异常,但这看起来很混乱。
答案 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。
答案 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上的通用算法就不会知道差异。