나는 -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을 제거하면 문제없이 코드로 컴파일됩니다.
답변
이것은 std::make_pair
사용 목적 이 아닙니다 . 템플릿 인수를 명시 적으로 지정해서는 안됩니다.
는 C ++ (11)는 std::make_pair
유형, 두 개의 인수를 T&&
하고 U&&
, 여기서 T
및 U
템플릿 형 매개 변수입니다. 실제로 다음과 같이 보입니다 (반환 유형 무시).
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::string
및 7
유형의를 rvalue입니다 int
. 함수 템플릿에 대한 템플릿 인수를 지정하지 않았으므로 인수가 무엇인지 파악하기 위해 템플릿 인수 추론이 수행됩니다.
s
lvalue 를에 바인딩 하기 T&&
위해 컴파일러는로 추론 T
하여 std::string&
유형의 인수를 생성합니다 std::string& &&
. 그러나 참조에 대한 참조가 없으므로이 “이중 참조”가 축소되어 std::string&
. s
일치합니다.
바인드로의 간단한 7
에 U&&
: 컴파일러는 추론 할 U
수 int
타입의 매개 변수 산출 int&&
에 성공적으로 결합, 7
그것이를 rvalue이기 때문입니다.
이러한 새로운 언어 기능에는 많은 미묘한 차이가 있지만 하나의 간단한 규칙을 따르면 매우 쉽습니다.
템플릿 인수가 함수 인수에서 추론 될 수 있다면 추론하도록 두십시오. 꼭 필요한 경우가 아니면 명시 적으로 인수를 제공하지 마십시오.
컴파일러가 어려운 작업을하도록하세요. 그러면 99.9 %의 시간이 어쨌든 정확히 원하는 것이됩니다. 원하는 것이 아닌 경우 일반적으로 식별하고 수정하기 쉬운 컴파일 오류가 발생합니다.