2017-02-11 81 views
1

假设我有以下Matrix模板类,并且需要将矢量表示为1 x RowSize或ColSize x 1矩阵(以便我可以重复使用与矢量兼容的许多矩阵运算符:乘以2基质,通过一个标量等)乘以矩阵:部分专业化和SFINAE

template <class T, size_t ColumnSize, size_t RowSize> 
struct Matrix { 
    T [ColumnSize][RowSize]; 
} 

我有两个问题:

1)如果我没有错的我可以通过偏特或矩阵的方法,使用SFINAE(例如实现这一当ColSize或RowSize为1时启用'length'方法)。提及的选项有哪些优缺点?

2)如果我选择去与局部特殊化,有没有办法来定义行和列向量一个专业化,而不是这样的:

template <class T, size_t ColumnSize> 
struct Matrix<T, ColumnSize, 1> { 
    T length() const; 

    T [ColumnSize][RowSize]; 
} 

template <class T, size_t RowSize> 
struct Matrix<T, 1, RowSize> { 
    T length() const; 

    T [ColumnSize][RowSize]; 
} 
+0

你应该给更多的上下文,否则你的问题是“基于意见”。 –

+0

如果你问及两者的缺点和优点,我认为这样会更好,因为这样更容易客观回答。 –

+0

建议:您可以使用'enum class VectorOrientation {ColumnVector,RowVector};'将参数定义为'template class Matrix'。那么我认为不会有歧义。 –

回答

1

这真的取决于需求是否是“一般矩阵必须不具有长度方法”(然后应使用SFINAE或继承),或者“不得在一般矩阵上调用”(然后在length内部的static_assert适用)。第三种选择是不做任何事情,并使length适用于通用矩阵,但还有其他操作只适用于矢量。

对于“一般矩阵必须不具有长度方法”。为了节省空间,我将使用int和更短的符号名称。而不是int_,你应该使用std::integral_constantint_包装是需要的,因为语言限制禁止专门处理更复杂的计算,如果该参数是非类型参数。因此,我们让paramer变成一种类型,并将其包含在其中。以下不使用SFINAE,而是继承。使用矢量混合基类的d(),您可以随时从混合类中访问矢量的数据。

template<int> struct int_; 

template<typename D, typename S> 
struct V { }; 

template<typename T, int A, int B> 
struct M : V<M<T, A, B>, int_<A * B>> { 
    T data[A][B]; 
}; 

template<typename T, int A, int B> 
struct V<M<T, A, B>, int_<A + B - 1>> { 
    int length() const { return A * B; } 

    M<T, A, B> *d() { return static_cast<M<T, A, B>*>(this); } 
    const M<T, A, B> *d() const { return static_cast<const M<T, A, B>*>(this); } 
}; 

现在这

int main() { 
    M<float, 1, 3> m1; m1.length(); 
    M<float, 3, 1> m2; m2.length(); 
    // M<float, 3, 2> m3; m3.length(); error 
} 

是 “length不能说是一个普通黑客帝国”,你可以使用 “static_assert”

template<typename T, int A, int B> 
struct M { 
    int length() const { 
     static_assert(A == 1 || B == 1, "must not be called on a matrix!"); 
     return A * B; 
    } 

    T data[A][B]; 
}; 

选择什么是最合适的

0

SFINAE只能根据自己的参数禁用模板声明。使用封闭类的参数禁用非模板成员函数(如length)有点不自然。该技术是这样的:

template <class T, size_t RowSize, size_t ColumnSize> 
struct Matrix { 
    // SFINAE turns a non-template into a template. 
    // Introduce a fake dependency so enable_if resolves upon function call. 
    template< typename size_t_ = size_t > 
    static constexpr 
    // Now write the actual condition within the return type. 
    std::enable_if_t< RowSize == 1 || ColumnSize == 1 
    , size_t_ > length() const; 
     { return RowSize * ColumnSize; } 

    T [ColumnSize][RowSize]; 
} 

如果你能忍受这个丑陋,那么你得到你想要什么:想要的类型,当条件不满足其完全消失的一个功能。没有其他支持是必要的。

另一方面,部分特化会影响整个类的定义。由于在每个部分专业化中复制整个类通常是很差的设计,因此Johannes所描述的使用继承。

只是为了增加一个替代他的答案,SFINAE可以在部分专业化中使用,以避免巧妙的代数和int_问题。

// Add "typename = void" for idiomatic class SFINAE. 
template<size_t RowSize, size_t ColumnSize, typename = void> 
struct maybe_vector_interface { }; // Trivial specialization for non-vectors 

// Partial specialization for vectors: 
template<size_t RowSize, size_t ColumnSize> 
struct maybe_vector_interface< RowSize, ColumnSize, 
    std::enable_if_t< RowSize == 1 || ColumnSize == 1 > > { 
    static constexpr int length() const 
     { return RowSize * ColumnSize; } 
}; 

template<typename T, size_t RowSize, size_t ColumnSize> 
struct Matrix 
    : maybe_vector_interface<RowSize, ColumnSize> { 
    T data[RowSize][ColumnSize]; 
}; 
+0

为什么downvoted ?. – Potatoswatter