2013-08-07 3 views
1
은 어떻게 든 폴 Preney 쓴 Expression templates and C++11의 표현 템플릿 코드에 의해 영감을, 나는 다음과 같은 테스트하기로 결정

:데이터 멤버와를 rvalue 생활 시간

template<typename T> 
struct X 
{ 
    X(T t) : t(std::forward<T>(t)) {} 

    T t; 
}; 

template<typename T> 
auto CreateX(T&& t) -> X<decltype(std::forward<T>(t))> 
{ 
    return X<decltype(std::forward<T>(t))>(std::forward<T>(t)); 
} 

을 다음, 나는 X<const vector<int>&>의 인스턴스를 생성하는 데 사용 및 X<vector<int>&&>으로는 다음과

int main() 
{ 
    int vec = {1,2,3,4}; 

    auto x1 = CreateX(vec); 
    auto x2 = CreateX(vector<int>{5,6,7,8}); 

    cout << "x1: "; for(auto x : x1.t) cout << x << " "; cout << endl; 
    cout << "x2: "; for(auto x : x2.t) cout << x << " "; cout << endl; 
} 

출력은 :

x1: 1 2 3 4 
x2: 0 0 33 0 0 0 7 8 

은 임시 vector<int>{5,6,7,8}의 수명이 연장되지 않고 rvalue- 참조 멤버 X::t이 다른 것과 바인딩된다는 것을 보여줍니다.

좋아요,이 대답은 What is the lifetime of the class data member which const reference to a rvalue?에서 예상되는 동작입니다.

그러나 여기서 질문은 Expression templates and C++11의 Paul Preney의 코드에서 rvalue-references 멤버가 존재하는 한 임시 벡터가 존재할 수 있다는 점이 다릅니다. 임시 직원이 생기는 케이스 2를보십시오.

분명히이 여기에 사용 된 동일한 구조이지만, 아마도 뭔가 빠져 있습니다.


는 편집 :

int main() 
{ 
    using namespace std; 

    auto expr = math_vector<3>{1.0, 1.1, 1.2} + math_vector<3>{2.0, 2.1, 2.2}; 

    cout << "vec1: "; for(int i = 0; i < 3; ++i) cout << expr.le()[i] << " "; cout << endl; 
    cout << "vec2: "; for(int i = 0; i < 3; ++i) cout << expr.re()[i] << " "; cout << endl; 
} 

그리고이 출력하는 유효한 코드가 밝혀 : R. 마르틴 페르난데스의 답변에 따라 아래, 나는 다음을 시도

vec1: 1.0 1.1 1.2 
vec2: 2.0 2.1 2.2 

따라서 식 템플릿에 저장된 참조는 분명히 매달리지 않습니다. 여기서 무슨 일이 일어나고있는거야?

+0

Nitpick : X의 생성자에서'std :: forward '은 단지'std :: move'를 말하는 장황한 방법 일뿐입니다. 'decltype (std :: forward (t))'도'T &&'를 말하는 매우 장황한 방법입니다. –

답변

3

방정식 템플릿의 Paul Preney '코드와 rvalue-references 멤버가 존재하는 한 임시 벡터를 허용하는 C++ 11에서 다른 점은 무엇입니까?

아무 것도 허용하지 않습니다.

전체 표현식의 끝까지, 로컬 참조 변수에 바인딩되지 않은 다른 임시 테이블과 같은 임시 벡터가 있습니다. 코드가 표현식 트리를 실제 math_vector으로 즉시 구체화하고 이후 임시 파일이 더 이상 필요하지 않기 때문에 Paul의 코드에서 충분합니다.

바울의 코드는 표현 템플릿 노드 (math_vector_expr)를 어디에도 저장하지 않지만 코드는 Xx2으로 저장합니다. 이는 auto의 알려진 문제입니다. 즉, 표현 템플릿을 사용할 때 잘못된 작업을 수행합니다. 바로 표현식 트리를 저장하기 때문에 결과를 즉시 저장할 수있는 참조를 포함하게됩니다.

완전히 명확하게하기 위해 다음과 같이 적절합니다.

math_vector<3> result = 
    math_vector<3>{1.0, 1.1, 1.2} + 
    math_vector<3>{2.0, 2.1, 2.2} + 
    math_vector<3>{3.0, 3.1, 3.2} + 
    math_vector<3>{4.0, 4.1, 4.2} 
; // no references are held to any temporaries past this point 

다음과 같지 않습니다. R. 마르틴 페르난데스는 그의 대답에 진술로 - -

math_vector_expr<3> result =  // or auto 
    math_vector<3>{1.0, 1.1, 1.2} + 
    math_vector<3>{2.0, 2.1, 2.2} + 
    math_vector<3>{3.0, 3.1, 3.2} + 
    math_vector<3>{4.0, 4.1, 4.2} 
; // result now holds references to those temporaries 
+0

@Martinho, 제 편집 좀보세요. 두 번째 스 니펫은 유효한 코드이며, 표현 템플릿의 참조에 액세스하여 내용을 인쇄 할 수도 있습니다. – Allan

+0

@Allan 예, 컴파일한다는 의미에서 "유효"합니다. 그러나 일할 것을 보장하지는 않습니다. 그 내용을 인쇄 할 수 있다는 것은 우연입니다. 때로는 매달린 참조에 여전히 액세스 할 수있는 것처럼 보일 수도 있지만, 이는 운이 좋았 기 때문에 발생했습니다. 즉, 동작이 정의되지 않았으며 사소한 변경이나 다른 컴파일러 플래그 또는 버전으로 작업하는 것을 중단 할 수 있습니다. 그것은 당신이 여기에 설명 된 것과 유사한 이유로 그들을 인쇄 할 수 있었던 일이 : http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794#6445794 –

+0

설명해 주셔서 감사합니다. – Allan

2

문제는 당신이 자신의 지시 대상을보다 오래 당신의 "식 트리"의 우변에 대한 참조를 캡처 할 수 있습니다. 해결책은 lvalue에 대한 참조 만 저장하고 rvalue 참조로 전달 된 객체를 직접 캡처하는 것입니다. 즉, (Live code at Coliru) 식 트리에 대신 참조하여 을 저장 : 사용자가 당신에게 좌변 참조를 전달

template<typename T> 
struct X 
{ 
    X(T t) : t(std::move(t)) {} 

    T t; 
}; 

// Capture lvalue references 
template<typename T> 
X<const T&> CreateX(const T& t) 
{ 
    return X<const T&>(t); 
} 

// Copy rvalue references 
template<typename T> 
typename std::enable_if<std::is_rvalue_reference<T&&>::value,X<T>>::type 
CreateX(T&& t) 
{ 
    return X<T>(std::move(t)); 
} 

경우는 지시 대상은 식 트리를 들보 다 오래 남았습니다 수 있도록 그녀의 책임입니다 목적.