簡體   English   中英

C ++隱式模板實例化

[英]C++ implicit template instantiation

我目前有類層次結構

MatrixBase -> DenseMatrix
           -> (other types of matrices)
           -> MatrixView -> TransposeView
                         -> DiagonalView
                         -> (other specialized views of matrices)

MatrixBase是一個抽象類,它強制實現者定義operator()(int,int)等等; 它代表二維數字數組。 MatrixView表示一種(可能是可變的)查看矩陣的方式,例如轉置它或采用子矩陣。 MatrixView的觀點是能夠說出類似的東西

Scale(Diagonal(A), 2.0)

Diagonal返回一個DiagonalView對象,它是一種輕量級適配器。

現在這是問題所在。 我將使用一個非常簡單的矩陣運算作為例子。 我想定義一個像這樣的函數

template <class T>
void Scale(MatrixBase<T> &A, const T &scale_factor);

這就是名字暗示的顯而易見的事情。 我希望能夠傳遞一個誠實到良好的非視圖矩陣,或者MatrixView的子類的MatrixView 上面寫的原型不適用於諸如此類的語句

Scale(Diagonal(A), 2.0);

因為DiagonalView通過返回的對象Diagonal是一個暫時的, Scale需要非const引用,這是不能接受暫時的。 有沒有辦法讓這項工作? 我試圖使用SFINAE,但我不太了解它,我不確定這是否能解決問題。 對我來說很重要的是,可以在不提供顯式模板參數列表的情況下調用這些模板化函數(我想要隱式實例化)。 理想情況下,上述陳述可以按照書面形式起作


編輯:(后續問題)

由於sbi在下面回答了關於右值引用和臨時值的問題,有沒有辦法定義兩個版本的Scale,一個為非視圖采用非const rvalue引用,另一個采用按值傳遞視圖? 問題是在編譯時區分這兩者,使隱式實例化起作用。


更新

我已經將類層次結構更改為

ReadableMatrix
WritableMatrix : public ReadableMatrix
WritableMatrixView
DenseMatrix : public WritableMatrix
DiagonalView : public WritableMatrixView

WritableMatrixViewWritableMatrix不同的原因是視圖必須由const引用傳遞,而矩陣本身必須由非const引用傳遞,因此訪問器成員函數具有不同的const。 現在像Scale這樣的函數可以定義為

template <class T>
void Scale(const WritableMatrixView<T> &A, const T &scale_factor);
template <class T>
void Scale(WritableMatrix<T> &A, const T &scale_factor){
    Scale(WritableMatrixViewAdapter<T>(A), scale_factor);
}

請注意,有兩個版本,一個用於const視圖,另一個用於實際矩陣。 這意味着對於像Mult(A, B, C)這樣的函數,我需要8次重載,但至少它可以工作。 然而,什么不起作用是在其他功能中使用這些功能。 你看,每個類似於View的類都包含一個成員View ,它正在View它; 例如,在表達式Diagonal(SubMatrix(A))Diagonal函數返回DiagonalView<SubMatrixView<T> >類型的對象,該對象需要知道A的完全派生類型。 現在,假設在Scale I中調用一些其他類似的函數,它接受基本視圖或矩陣引用。 這將失敗,因為所需的View的構造需要Scale的參數的派生類型; 它沒有的信息。 仍在努力尋找解決方案。


更新

我已經使用了Boost的enable_if的本土版本,可以在像Scale這樣的函數的兩個不同版本之間進行選擇。 它歸結為標記我的所有矩陣和視圖類,其中包含額外的typedef標記,指示它們是可讀寫的還是視圖或非視圖。 最后,我仍然需要2 ^ N個重載,但現在N只是非const參數的數量。 對於最終結果,請參見此處 (不太可能再次進行認真修改)。

這與模板無關。 你的榜樣

Scale(Diagonal(A), 2.0);

可以推廣到

f(g(v),c);

在C ++ 03中,這需要f()的第一個參數為每個副本或每個const引用傳遞。 原因是g()返回一個臨時值,一個右值。 但是,rvalues只綁定到const引用,而不綁定到非const引用。 這與模板,SFINAE,TMP或其他內容無關。 這就是語言(目前)的方式。

這背后還有一個基本原理:如果g()返回一個臨時的,並且f()修改了那個臨時的,那么沒有人有機會“看到”修改后的臨時。 因此修改是徒勞的,整個事情很可能是一個錯誤。

據我所知,在你的情況下, g()的結果是一個臨時的,它是一個其他對象( v )的視圖,所以修改它會修改v 但是如果是這種情況,在當前的C ++中, g()的結果必須是const (因此它可以綁定到const引用或者必須被復制。因為const “聞起來”對我來說錯了,使得該視圖便宜復制可能是最好的事情。

但是,還有更多。 C ++ 1x將引入所謂的右值引用。 我們所知的“引用”將被分為左值引用或右值引用。 您將能夠使用rvalue引用函數,甚至基於“l / rvalue-ness”進行重載。 這被認為是允許類設計者重載rvalue右側的復制ctor和賦值,並讓它們“竊取”右側的值,這樣復制rvalues會更便宜。 但你可以用它來讓Scale取一個rvalue並修改它。

不幸的是,你的編譯器很可能還不支持rvalue引用。


編輯(后續問題)

你不能用f(T&)重載f(T) f(T&)來達到你想要的效果。 雖然只有前者將用於rvalues,但是左值可以同樣地綁定到任一個參數,因此使用左值調用該f是不明確的並且導致編譯時錯誤。

但是,為DiagonalView重載有什么問題:

template <class T>
void Scale(MatrixBase<T> &matrix, const T &scale_factor);

template <class T>
void Scale(DiagonalView<T> view, const T &scale_factor);

有什么我想念的嗎?


另一個編輯

我需要一個可笑的大量重載,因為目前有超過5個視圖,並且有幾十個函數,如Scale。

然后,您需要將可以以相同方式處理的那些類型組合在一起。 您可以使用一些簡單的模板元素來進行分組。 脫離我的頭頂:

template<bool B>
struct boolean { enum { result = B }; };

template< typename T >
class some_matrix {
  public:
    typedef boolean<false> is_view;
  // ...
};

template< typename T >
class some_view {
  public:
    typedef boolean<true> is_view;
  // ...
};

namespace detail {
  template< template<typename> class Matrix, typename T >
  void Scale(Matrix<T>& matrix, const T& scale_factor, boolean<true>)
  {
    /* scaling a matrix*/
  }
  template< template<typename> class Matrix, typename T >
  void Scale(View<T>& matrix, const T& scale_factor, boolean<true>)
  {
    /* scaling a view */
  }
}

template< template<typename> class Matrix, typename T >
inline void Scale(Matrix<T>& matrix, const T& scale_factor)
{
  detail::Scale( matrix, scale_factor, typename Matrix<T>::is_view() );
}

這種特殊的設置/分組可能不完全符合您的需求,但您可以以適合自己的方式設置這樣的設置。

解決這個問題的一種簡單方法是使用boost::shared_ptr< MatrixBase<T> >而不是引用。

可能是,你應該使用const

template <class T>
void Scale(const MatrixBase<T> &A, const T &scale_factor);

你是限制Scale的第一個參數的類型,但你可以讓編譯器自己找出適合的類型,如下所示:

template <class M,class T>
void Scale(M A, const T &scale_factor);

不要使用引用,按值傳遞。

如果需要,讓copy elision為您進行優化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM