2017-12-05 10 views
1

매트릭스 형식이 템플릿 매개 변수 인 매트릭스 함수 계산을 목표로 C++ 템플릿 함수를 작성했습니다. armadillo 라이브러리와 함께 사용할 때 컴파일이 예기치 않게 실패합니다. 저는 armadillo 8.300과 gcc 7.2.0을 사용하고 있습니다. 다음은이 문제를 설명하는 테스트 프로그램입니다. armadillo 라이브러리를 사용하여 예기치 않은 (잘못된) 템플릿 공제

#include <armadillo> 

arma::Mat<double> sq(const arma::Mat<double>& M) 
{ 
    arma::Mat<double> res(M); 
    res = res * M; 
    return res; 
} 

template <class MatrixClass> 
MatrixClass sqgen(const MatrixClass& M) 
{ 
    MatrixClass res(M); 
    res = res * M; 
    return res; 
} 

int main() 
{ 
    arma::Mat<double> id(3, 3, arma::fill::eye); 
    arma::Mat<double> m(3, 3, arma::fill::ones); 

    arma::Mat<double> m2(sq(m)); 
    arma::Mat<double> m_id2(sq(id - m)); 

    arma::Mat<double> m2gen(sqgen(m)); 
    arma::Mat<double> m_id2gen(sqgen(id - m)); // Error here 
    return 0; 
} 

은 그림을 위해 나는 두 가지 기능 sq와 본질적으로 같은 일을 할 sqgen을 정의했다. sq은 템플릿 매개 변수 MatrixClassarma::Mat<double> 일 때 sqgen의 명시적인 인스턴스입니다.

test.cpp: In instantiation of ‘MatrixClass sq(const MatrixClass&) [with MatrixClass = arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>]’: 
test.cpp:24:36: required from here 
test.cpp:14:7: error: no match for ‘operator=’ (operand types are ‘arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>’ and ‘arma::enable_if2<true, const arma::Glue<arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::glue_times> >::result {aka const arma::Glue<arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::glue_times>}’) 
    res = res * M; 
    ~~~~^~~~~~~~~ 
In file included from /usr/include/armadillo:204:0, 
       from test.cpp:1: 
/usr/include/armadillo_bits/eGlue_bones.hpp:22:7: note: candidate: arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>& arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>::operator=(const arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>&) <deleted> 
class eGlue : public Base<typename T1::elem_type, eGlue<T1, T2, eglue_type> > 
     ^~~~~ 
/usr/include/armadillo_bits/eGlue_bones.hpp:22:7: note: no known conversion for argument 1 from ‘arma::enable_if2<true, const arma::Glue<arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::glue_times> >::result {aka const arma::Glue<arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::glue_times>}’ to ‘const arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>&’ 

문제는 sqgen의 마지막 호출에있다 :

g++ -std=c++14 -Wall -pedantic -O3 -o test test.cpp -lstdc++ -larmadillo 

와 컴파일 오류를주고 실패. sqgen(m)으로 전화해도 문제가되지 않지만 sqgen(id - m)으로 전화하면 오류가 발생합니다. 대신 sq(id - m) 호출을 사용하는 것은 완전히 합법적입니다. 기능 sqgen이어야하기 때문에, sq 정확하게 일치 위의 코드에서, 나는 컴파일러는 sqgen(id - m)에 올바르게 템플릿 매개 변수 MatrixClass 추론하지 않는 가정.

arma::Mat<double> m_id2gen(sqgen(arma::Mat<double>(id - m))); 

코드로

arma::Mat<double> m_id2gen(sqgen(id - m)); 

대체에 따라 사실 이 제대로 컴파일합니다.

답변

2

템플릿 패턴 일치는 정확한 유형 (정확한 유형이 일치하지 않는 경우 상위 유형의 유형)과 일치합니다.

오버로드 분석은 변환 할 수있는 정확한 유형, 상위 유형 및 유형과 일치합니다.

확율 arma::Mat의 작업을 행렬로 변환 할 수있는 표현 템플릿을 초래할 것을, 그러나 자체는 행렬 없습니다.그것들은 여러분이 행렬 수학의 전체 행렬을 취할 수 있도록하며, 모든 것을 행렬로 실제로 변환 할 때까지는 효율적으로 수행하지 않습니다.

sqgen은 무엇이든 걸리기 때문에이 경우 값이 두 행렬의 차이 인 표현 템플릿을 사용하려고합니다.

그런 다음 인수가없는 임시 표현식 템플리트 인스턴스를 작성하고이를 다른 표현식 템플리트와 곱하고 할당 한 후 리턴하십시오. 이들 중 어떤 것도 표현 템플릿에 의미가 없습니다.

이것은 표현식 템플릿 및 일반 코드의 알려진 문제점입니다. 일반적으로 표현 템플릿을 강제로 평가할 수있는 방법이 있습니다. 그것들을 행렬에 모으는 것은 그것 (그리고 어떻게 동작하는가)이며, 그것들을 캐스팅하는 것이고,이 경우 타입을 명명하지 않고도 수행하는 .eval() 멤버 함수가 있습니다. 값이 만료 된 지역 매트릭스에 의해 결정되었다 작동하지 않을 것 같다 표현 템플릿을 반환 sqgen``때문에

그래서, 나는이 작동 의심

arma::Mat<double> m_id2gen(sqgen((id - m).eval())); 
0

컴파일러가 올바르게 작동합니다. Armadillo는 승수 재 배열, 평가 지연 등과 같은 매트릭스 관련 표현을 크게 최적화합니다. 이것은 모두 템플릿 메타 프로그래밍을 통해 수행됩니다. 아르마딜로 행렬 클래스는 다른 행렬을 취하여 데이터를 복사하는 복사 할당 연산자 (예 : mat::operator=())를 제공합니다. 그러나이 표현 템플릿 중 하나를 취하는 operator=()의 과부하는 없습니다. 따라서 오류는 operand types are 'eGlue<...>...' 정도입니다.

이에 대한 빠른 수정이 말했다 표현의 평가를 강제로 어떤 식의 말에 .eval()를 추가하는 것입니다. 그래서 당신은 할 줄 : 단지

res = (res * M).eval(); 
return res; 

또는를 :

return (res * M).eval(); // I'm actually not sure if the eval() is necessary here. 

또 다른 옵션도 잘 작동해야하는, 현재 위치에서 곱셈을 수행하려고하는 것입니다. 다음과 같이 :

res *= M; 
return res; 
+0

을 시도합니다. – Yakk

+0

@Yakk 좋아, 잘 잡는다. 귀하의 대답이 더 적절합니다. – bnaecker

+0

제안 된 솔루션 중 어느 것도 작동하지 않습니다. 컴파일러는 eval()을 추가하더라도''error : 'operator ='와 일치하지 않습니다 (피연산자 유형은 'arma :: eGlue , arma :: Mat , arma :: eglue_minus>' 및 'arma :: Mat ')'''. Operator * = one은 유사한 오류를 obatins합니다.''error : 'operator * ='와 일치하지 않습니다 (피연산자 유형은 'arma :: eGlue , arma :: Mat , arma :: eglue_minus> '와'const arma :: eGlue , arma :: Mat , arma :: eglue_minus> ')''' – francesco