2017-02-11 56 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