2009-11-16 18 views
7

내가 현재C++에서 암시 적 템플릿 인스턴스화

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

MatrixBase 같은 클래스 계층 구조를 가지고있다 연산자() (INT, INT)와 같은 것을 정의 구현을 강제하는 추상 클래스; 2 차원 숫자 배열을 나타냅니다. MatrixView은 행렬을 전치 시키거나 부분 행렬을 취하는 것과 같이 행렬을 바라 보는 (아마도 변경 가능한) 방법을 나타냅니다. MatrixView의 점은 말할 수있을 것입니다 뭔가 Diagonal 경량 어댑터의 일종 인 DiagonalView 객체를 반환

Scale(Diagonal(A), 2.0) 

있다.

이제 질문이 있습니다. 아주 간단한 행렬 연산을 예제로 사용하겠습니다.

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

과 같은 함수를 정의하고 싶습니다. 이름에서 알 수있는 분명한 사실은 있습니다. 솔직히 - 투 - 미스 비 - 뷰 행렬 또는 MatrixView의 서브 클래스 인스턴스를 전달할 수 있기를 원합니다. Diagonal에 의해 반환 된 DiagonalView 객체가 일시적이며, Scale 임시을 받아 들일 수없는 const가 아닌 참조를 필요하기 때문에 위의 기록으로 프로토 타입은

Scale(Diagonal(A), 2.0); 

으로 문이 작동하지 않습니다. 이 일을 할 수있는 방법이 있습니까? 나는 SFINAE를 사용하려고 시도했지만, 모든 것을 잘 이해하지 못하고 문제가 해결 될지 확실하지 않습니다. 이러한 템플릿 함수는 명시 적 템플릿 인수 목록을 제공하지 않고 호출 할 수 있다는 것이 중요합니다 (암시 적 인스턴스 생성을 원합니다). 위의 내용은 이상적으로 서면으로 작동 할 수 있습니다.


편집 : (후속 질문)

SBI이를 rvalue 참조 및 임시직에 대해 아래의 반응으로, 규모의 두 가지 버전을 정의하는 방법이 있나요, 대한 const가 아닌를 rvalue 참조를 필요 하나 뷰가 아닌보기 및 값별보기가 필요한보기? 문제는 암시 적 인스턴스화가 작동하는 방식으로 컴파일 타임에이 둘을 구별하는 것입니다.


업데이트

나는 동안,

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

WritableMatrixViewWritableMatrix 구별되는 이유보기가 const를 참조로 주위에 전달해야한다는 것입니다에 클래스 계층 구조를 변경 한 행렬 자체는 const가 아닌 ref로 전달되어야하므로 접근 자 멤버 함수는 다른 const를 갖습니다. 이제 스케일 등의 기능을 두 가지 버전하는 const를보기위한 하나의 실제 행렬에 대한 const가 아닌 버전이 있음을

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); 
} 

참고로 정의 할 수 있습니다. 이것은 Mult(A, B, C)과 같은 함수를 의미합니다. 8 오버로드가 필요하지만 적어도 작동합니다. 하지만 작동하지 않는 기능은 다른 기능에서 이러한 기능을 사용하는 것입니다.보시다시피, 각 View- 같은 클래스에는보고있는 내용의 View 멤버가 포함되어 있습니다. 예를 들어 Diagonal(SubMatrix(A))이라는 식에서 Diagonal 함수는 A의 완전 파생 형식을 알아야하는 DiagonalView<SubMatrixView<T> > 유형의 개체를 반환합니다. 이제 Scale 내에서 기본보기 또는 행렬 참조 중 하나를 사용하는 다른 함수를 호출한다고 가정합니다. 필요한 것의 생성이 Scale의 인수의 파생 된 유형을 필요로하기 때문에 실패 할 것입니다. 정보가 없다. 아직도이 문제에 대한 해결책을 찾아야합니다.


업데이트

내가 Scale 같은 함수의 두 가지 버전 중 하나를 선택 효과적으로 부스트의 enable_if의 자체 개발 버전입니다 무엇을 사용하고 있습니다. 그것은 모든 행렬에 라벨을 붙이고, 읽기 쉽고 쓰기가 가능하고 뷰인지 아닌지를 나타내는 여분의 typedef 태그를 사용하여 클래스를 표시합니다. 결국 2^N 오버로드가 여전히 필요하지만 이제 N은 비 const 인수의 수입니다. 최종 결과는 here을 참조하십시오 (심각하게 다시 개정되지는 않습니다).

+0

Scale()이 const 참조에 의한 매개 변수를 허용하지 않는 특별한 이유가 있습니까? – Naveen

+0

스케일은 실제로 인수를 확장해야하므로 const 일 수 없습니다. –

+1

스케일이 왜 임시 매트릭스를 수정합니까? –

답변

1

이 문제를 쉽게 해결할 수있는 방법은 참조 대신 boost::shared_ptr< MatrixBase<T> >을 사용하는 것입니다.

0

이 경우 const을 사용해야합니다. ?

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

아니요 const가 아니어야합니다. Scale이 A를 수정합니다. –

+0

@Victor : 스케일이 A를 수정하면 임시로 넘겨서는 안됩니다. 그러나, 만약 당신이 정말로 그것을하고 싶고 당신의 플랫폼이 윈도우라면, Visual Studio로 비 const 레퍼런스로 코드를 컴파일 할 수 있습니다. 그러나이 경우 전달 된 객체를 수정하려고하면 어떤 일이 발생할지 확신 할 수 없습니다. – Naveen

0

당신은 스케일의 첫 번째 인수의 유형을 제한하고 있지만이 같은 유형 자체에 적합 할 것입니다 무슨 밖으로 컴파일러 그림하도록 할 수 있습니다 :

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

예, 할 수는 있지만 다른 문제가 있습니다. 또한 VectorBase, Vector, VectorView 등이 있습니다. 그리고 Scale은 행렬 대신 벡터에서 다르게 작동해야합니다. 아마도이 추가 합병증을 통합하는 방법을 제안 할 수 있습니다. –

+0

추가 문제 : A는 값으로 전달 될 수 없습니다. 잠재적으로 거대한 사본이 될 수 있습니다. 둘째, 참조로 전달 된 경우 일반 매트릭스가 잘 전달되는 문제로 돌아가지만 뷰는 스택의 임시 테이블이므로 작동하지 않습니다. –

0

사용하지 마십시오을 참조, 가치 전달.

필요한 경우 copy elision에서 최적화를 수행합니다.

7

이것은 템플릿과 관련이 없습니다. 귀하의 예를

Scale(Diagonal(A), 2.0); 

이 사본 당 또는 const 기준에 따라 전달 중 하나에 f() 첫 번째 매개 변수를 필요로 C++ 03에서

f(g(v),c); 

에 일반화 될 수있다. 그 이유는 g()이 임시 값을 반환하기 때문입니다. 그러나 rvalues는 const 참조에만 바인딩되지만 비 const 참조에는 바인딩되지 않습니다. 이는 템플리트, SFINAE, TMP 또는 기타 다른 것들과 관련이 없습니다. 그것은 언어 (현재)입니다.

g()이 임시를 반환하고 f()이 임시를 수정하면 아무도 수정 된 임시를 볼 기회가 없습니다. 따라서 수정 작업은 헛된 일이며 모든 일은 오류 일 가능성이 큽니다.

내가 이해 한대로 g()의 결과는 다른 객체 (v)에 대한보기 인 임시이므로 v을 수정합니다. 그러나 그 경우 현재의 C++에서 g()의 결과는 const이어야합니다 (const 참조에 바인딩되거나 복사되어야합니다). const이 "나에게"냄새를 맡으므로 그 값싼 사본을 복사하는 것이 좋습니다. 아마도 가장 좋은 것이 겠지요.

그러나 더 많은 것이 있습니다. C++ 1x에서는 rvalue 참조를 소개합니다. 우리가 "참조"로 알고있는 것은 왼쪽 값 참조 또는 오른쪽 값 참조로 나뉘어집니다. rvalue 참조를 취하고 "l/rvalue-ness"를 기반으로 오버로드하는 함수를 가질 수 있습니다. 이것은 클래스 설계자가 오른쪽 값에 대해 복사기와 할당을 오버로드하여 오른쪽 값을 "도용"하도록하여 복사 값이 더 저렴해질 수 있도록하기 위해 생각되었습니다. 하지만 아마도 이것을 Scale에 부여하고 그 값을 수정하면됩니다.

불행히도 컴파일러는 아직 rvalue 참조를 지원하지 않을 가능성이 큽니다.


편집 (후속 질문)는 :

당신은 당신이 원하는 것을 달성하기 위해 f(T&)f(T)를 오버로드 할 수 없습니다. 전자는 rvalues로만 사용되지만 lvalues는 두 인수를 동등하게 잘 바인딩 할 수 있으므로 l 값을 사용하여 f을 호출하면 모호하며 결과적으로 컴파일 타임 오류가 발생합니다. 내가 부족 아무것도

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

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

있습니까 :

그러나, DiagonalView에 대한 과부하를 가진 문제점은 무엇입니까?


또 다른 편집 : 현재 5 개 이상의 전망이 있고, 규모 등 수십 기능이 있기 때문에

내가 다음 과부하의 엄청나게 많은 수의 필요합니다.

그런 다음 동일한 방식으로 처리 할 수있는 유형을 그룹화해야합니다. 그룹화를하기 위해 간단한 템플릿 메타 작업을 사용할 수 있습니다. 내 머리 위로 떨어져 :

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()); 
} 

이 특정 설정/그룹 정확하게 사용자의 요구에 맞지 않을 수도 있지만, 당신은 자신에 맞는 방법으로이 같은 설정 무언가를 할 수 있습니다.

+0

이 점을 완전히 이해했지만 다른 추가 질문을 추가했습니다. –

+0

현재 5 개 이상의 뷰가 있고 Scale과 같은 수십 개의 함수가 있기 때문에 엄청나게 많은 오버로드가 필요합니다. –

관련 문제