지정된 템플릿 매개 변수가있는 C ++ 11 make_pair가 컴파일되지 않음


84

나는 -std = c ++ 11이 활성화 된 상태에서 g ++ 4.7 (나중 스냅 샷 중 하나)을 가지고 놀았습니다. 기존 코드베이스의 일부를 컴파일하려고했는데 실패한 경우가 다소 혼란 스러웠습니다.

누군가가 무슨 일이 일어나고 있는지 설명해 주시면 감사하겠습니다.

코드는 다음과 같습니다.

#include <utility>
#include <iostream>
#include <vector>
#include <string>

int main ( )
{
    std::string s = "abc";

    // 1 ok
    std::pair < std::string, int > a = std::make_pair ( s, 7 );

    // 2 error on the next line
    std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );

    // 3 ok
    std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );

    return 0;
}

make_pair가 (1) 케이스로 사용 된다는 것을 이해 합니다 (유형을 지정하면 (3)을 사용하는 것이 좋습니다).이 경우 실패하는 이유를 이해하지 못합니다.

정확한 오류는 다음과 같습니다.

test.cpp: In function ‘int main()’:
    test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’
    test.cpp:11:83: note: candidate is:
    In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
                 from test.cpp:1:
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note:   template argument deduction/substitution failed:
    test.cpp:11:83: note:   cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’

다시, 여기서 질문은 "무슨 일이 일어나고 있는가?"입니다. 템플릿 사양을 제거하여 문제를 해결할 수 있다는 것을 알고 있지만 여기에서 무엇이 실패했는지 알고 싶습니다.

  • g ++ 4.4는 문제없이이 코드를 컴파일합니다.
  • -std = c ++ 11을 제거하면 문제없이 코드로 컴파일됩니다.

6
훌륭한 질문입니다. C ++ 11의 미묘한 브레이킹 체인지의 또 다른 예는 construction 의 브레이킹 체인지std::vector 와 유사합니다 . 적어도 이것은 컴파일러 오류를 생성하고 의미 체계의 자동 변경이 아닙니다.
James McNellis 2012 년

1
정수 변수가있는 경우 i. i와 다른 물건과 짝을 이루고 싶습니다. 얼마나 정확하게 makepair를 호출해야합니까? 1) make_pair <* i, obj> 2) int && j = i; make_pair <j, obj>? 둘 다 작동하지 않습니다. 올바른 방법은 무엇입니까?
PHcoDer

답변:


135

이것은 std::make_pair사용 목적 이 아닙니다 . 템플릿 인수를 명시 적으로 지정해서는 안됩니다.

는 C ++ (11)는 std::make_pair유형, 두 개의 인수를 T&&하고 U&&, 여기서 TU템플릿 형 매개 변수입니다. 실제로 다음과 같이 보입니다 (반환 유형 무시).

template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);

std::make_pair템플릿 유형 인수 를 호출 하고 명시 적으로 지정하면 인수 추론이 발생하지 않습니다. 대신 형식 인수가 템플릿 선언으로 직접 대체되어 다음을 생성합니다.

[return type] make_pair(std::string&& argT, int&& argU);

이러한 매개 변수 유형은 모두 rvalue 참조입니다. 따라서 rvalue에만 바인딩 할 수 있습니다. 전달하는 두 번째 인수에는 문제가되지 않습니다 7. 이는 rvalue 표현식이기 때문입니다. s그러나은 lvalue 표현식입니다 (임시적이지 않고 이동되지 않음). 이는 함수 템플릿이 인수와 일치하지 않음을 의미하므로 오류가 발생합니다.

그렇다면 템플릿 인수 목록에 무엇이 T있고 무엇인지 명시 적으로 지정하지 않으면 왜 작동 U합니까? 간단히 말해 rvalue 참조 매개 변수는 템플릿에서 특별합니다. 부분적으로 참조 축소 라는 언어 기능으로 인해 유형의 rvalue 참조 매개 변수 ( A&&여기서는 A템플릿 유형 매개 변수)는 모든 종류의 A.

A가 lvalue, rvalue, const-qualified, volatile-qualified 또는 unqualified 인지 여부는 중요하지 않습니다 A&&(다시 말하지만 A자체가 템플릿 매개 변수 인 경우에만 ).

귀하의 예에서는 다음과 같이 호출합니다.

make_pair(s, 7)

여기에, s유형의 좌변입니다 std::string7유형의를 rvalue입니다 int. 함수 템플릿에 대한 템플릿 인수를 지정하지 않았으므로 인수가 무엇인지 파악하기 위해 템플릿 인수 추론이 수행됩니다.

slvalue 를에 바인딩 하기 T&&위해 컴파일러는로 추론 T하여 std::string&유형의 인수를 생성합니다 std::string& &&. 그러나 참조에 대한 참조가 없으므로이 "이중 참조"가 축소되어 std::string&. s일치합니다.

바인드로의 간단한 7U&&: 컴파일러는 추론 할 Uint타입의 매개 변수 산출 int&&에 성공적으로 결합, 7그것이를 rvalue이기 때문입니다.

이러한 새로운 언어 기능에는 많은 미묘한 차이가 있지만 하나의 간단한 규칙을 따르면 매우 쉽습니다.

템플릿 인수가 함수 인수에서 추론 될 수 있다면 추론하도록 두십시오. 꼭 필요한 경우가 아니면 명시 적으로 인수를 제공하지 마십시오.

컴파일러가 어려운 작업을하도록하세요. 그러면 99.9 %의 시간이 어쨌든 정확히 원하는 것이됩니다. 원하는 것이 아닌 경우 일반적으로 식별하고 수정하기 쉬운 컴파일 오류가 발생합니다.


6
이것은 매우 훌륭하고 포괄적 인 설명입니다. 감사합니다!
vmpstr

1
@James-다른 기사의 "단순한 규칙"입니까 아니면 읽어야 할 대답입니까?
Michael Burr

4
@MichaelBurr : 아뇨, 방금 만들었습니다. :-) 그래서, 나는 그것이 사실이기를 바랍니다! 사실이라고 생각합니다 ... 그 규칙은 거의 항상 저에게 효과적입니다.
James McNellis

1
@James : 감사합니다. 그 주변의 '인용구 상자'는 그것이 원래 다른 곳에 쓰여진 것일 수도 있다고 생각하게했습니다. 이 답변은 정말 유익했고 다른 곳에서 누락 된 것이 없는지 확인하고 싶었습니다.
Michael Burr

2
이것은 튜플에도 적용됩니까?
Ferruccio
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.