2016-11-16 1 views

답변

2

std::tie(a,b) = {b, a}; 

컴파일하지 않는 이유는 무엇입니까?

{}은 할당의 오른쪽 부분에만 operator=의 논리적이지 않은 생성자를 호출 할 수 있습니다. 사용할

operator= 과부하은 :

tuple& operator=(const tuple& other); 
tuple& operator=(tuple&& other); 
template< class... UTypes > 
tuple& operator=(const tuple<UTypes...>& other); 
template< class... UTypes > 
tuple& operator=(tuple<UTypes...>&& other); 
template< class U1, class U2 > 
tuple& operator=(const pair<U1,U2>& p); 
template< class U1, class U2 > 
tuple& operator=(pair<U1,U2>&& p); 

template 연산자 과부하가 {}에서 그들의 타입을 추론 할 수 없다 (참고 : 이는 C++ 17 변경할 수있다), 이탈 :

tuple& operator=(const tuple& other); 
tuple& operator=(tuple&& other); 

곳 이 경우 tuplestd::tuple<int&, int&>입니다.

tuple<Ts...>에 대한 tuple 생성자는 요소 방식으로 완벽한 전달은 explicit (해당 목록에서 # 3)입니다. {}은 명시 적 생성자를 호출하지 않습니다.

조건부 비표시 생성자는 Ts const&...을 사용합니다. Ts이 복사 불가능하고 int&이 복사 불가능한 경우 존재하지 않습니다.

따라서 {int&, int&}에서 구성 할 실제 유형이없고 과부하 해결에 실패합니다.


표준이이 문제를 해결하지 않는 이유는 무엇입니까? 글쎄, 우리 스스로 할 수있어!

이 문제를 해결하려면유형이 모두 참조 인 경우에만 tuple에 특수한 (Ts...) 명시 적 생성자를 추가해야합니다.

우리가 장난감 튜플 작성하는 경우 :

struct toy { 
    std::tuple<int&, int&> data; 
    toy(int& a, int& b):data(a,b) {} // note, non-explicit! 
}; 
toy toy_tie(int& a, int& b) { return {a,b}; } 

및 사용을, 당신이 알 수

std::tie(a, b) = {b, a}; 

컴파일 및 실행합니다. a%bint&에 바인딩 할 수 없기 때문에

그러나

,

std::tie(a, b) = { b, a % b }; 

은하지 않습니다.

우리는 다음과 toy 보강 할 수 있습니다 : (. + 특별 회원 기능을 디폴트 예상대로 template<class...>, 그것은 특별한 멤버 함수보다 우선 순위가 보장)

template<class...> 
toy& operator=(std::tuple<int, int> o) { 
    data = o; 
    return *this; 
} 

합니다.

할당자를 {int,int}에서 할당 할 수 있습니다. 우리는 그것을 실행하고 ... 잘못된 결과를 얻습니다. 5,20의 gcd는 20입니다. 무엇이 잘못 되었습니까? 참조에 바인드 모두 ab

toy_tie(a, b) = std::tie(b, a); 

안전 코드가 아닙니다, 그것은

toy_tie(a, b) = { b, a }; 

가하는 일입니다.

간단히 말해서,이 권리를하는 것은 까다 롭습니다. 이 경우 안전을 위해 할당하기 전에 사본을 오른쪽으로 가져 가야합니다. 언제 사본을 가져야하는지 알지 못하는 경우 또한 까다 롭습니다.

이 작업을 수행하면 암시 적으로 오류가 발생합니다. 어떤 의미에서는 우연히 작동하지 않지만 가능하다면 해결하는 것은 나쁜 생각처럼 보입니다.

live example.

+0

위대한 설명 .. 나는 Python의 다중 할당 (예 : a, b = b, a % b)에 영향을 받았지만이 안전하게 당신은 매번 (파이썬 않습니다) –

4

std::initializer_list 항목의 균일 모음입니다, std::tuple이기종된다. std::initializer_list에 대해 std::tuple::operator=을 정의하는 것이 타당한 유일한 경우는 튜플이 동질성이며 이니셜 라이저 목록과 크기가 같은 경우입니다. 이는 드문 경우입니다.

(Additional information in this question.)


솔루션/해결 방법 : 대신 std::make_tuple를 사용할 수 있습니다

int greatestCommonDivisor(int a, int b) 
{ 
    if (b > a) 
     std::tie(a, b) = std::make_tuple(b, a); 
    while (b > 0) 
     std::tie(a, b) = std::make_tuple(b, a % b); 
    return a; 
} 

... 또는 std::tuple의 생성자를 C++ 17 에 (감사 Template argument deduction for class templates에) :

int greatestCommonDivisor(int a, int b) 
{ 
    if (b > a) 
     std::tie(a, b) = std::tuple{b, a}; 
    while (b > 0) 
     std::tie(a, b) = std::tuple{b, a % b}; 
    return a; 
} 
+1

'= {blah}'는 항상 이니셜 라이저 목록을 참조하는 것은 아닙니다. 그것은 건설 일 수있다. – Yakk

관련 문제