Po prostu bawiłem się g ++ 4.7 (jedną z późniejszych migawek) z włączoną opcją -std = c ++ 11. Próbowałem skompilować część mojego istniejącego kodu i jeden przypadek, który się nie powiódł, nieco mnie zmylił.
Byłbym wdzięczny, gdyby ktoś mógł wyjaśnić, co się dzieje.
Oto kod:
#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;
}
Rozumiem, że make_pair ma być używany jako przypadek (1) (jeśli określę typy, równie dobrze mogę użyć (3)), ale nie rozumiem, dlaczego w tym przypadku zawodzi.
Dokładny błąd to:
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>&&’
Ponownie, pytanie brzmi po prostu „co się dzieje?” Wiem, że mogę rozwiązać problem, usuwając specyfikację szablonu, ale chcę tylko wiedzieć, co tu się nie udaje pod okładkami.
- g ++ 4.4 kompiluje ten kod bez żadnych problemów.
- Usunięcie -std = c ++ 11 również kompiluje się z kodem bez żadnych problemów.
std::vector
konstrukcji . Przynajmniej ten powoduje błąd kompilatora, a nie cichą zmianę semantyki.Odpowiedzi:
To nie jest sposób, w jaki
std::make_pair
ma być używany; nie powinieneś jawnie określać argumentów szablonu.C ++ 11
std::make_pair
przyjmuje dwa argumenty typuT&&
iU&&
, gdzieT
iU
są parametrami typu szablonu. Skutecznie wygląda to tak (ignorując zwracany typ):template <typename T, typename U> [return type] make_pair(T&& argT, U&& argU);
Gdy wywołujesz
std::make_pair
i jawnie określasz argumenty typu szablonu, nie ma miejsca odejmowanie argumentów. Zamiast tego argumenty typu są zastępowane bezpośrednio w deklaracji szablonu, dając:[return type] make_pair(std::string&& argT, int&& argU);
Zauważ, że oba te typy parametrów są odwołaniami do wartości r. Dlatego mogą wiązać się tylko z rwartościami. Nie stanowi to problemu dla drugiego przekazywanego argumentu
7
, ponieważ jest to wyrażenie r-wartości.s
jednak jest wyrażeniem l-wartości (nie jest tymczasowe i nie jest przenoszone). Oznacza to, że szablon funkcji nie pasuje do twoich argumentów i dlatego otrzymujesz błąd.Dlaczego więc to działa, jeśli nie określisz wyraźnie, co
T
i coU
znajduje się na liście argumentów szablonu? Krótko mówiąc, parametry odniesienia rvalue są specjalne w szablonach. Częściowo ze względu na funkcję języka zwaną zwijaniem referencji, parametr odwołania rvalue typuA&&
, gdzieA
jest parametrem typu szablonu, może być powiązany z dowolnym rodzajemA
.Nie ma znaczenia, czy
A
jest to wartość l, wartość r, kwalifikowana jako stała, kwalifikowana jako zmienna czy niekwalifikowana,A&&
może wiązać się z tym obiektem (ponownie, wtedy i tylko wtedy, gdyA
sam jest parametrem szablonu).W Twoim przykładzie dzwonimy:
make_pair(s, 7)
Tutaj
s
jest lwartość typustd::string
i7
jest rwartością typuint
. Ponieważ nie określasz argumentów szablonu dla szablonu funkcji, wykonywana jest dedukcja argumentów szablonu w celu ustalenia, jakie są argumenty.Aby powiązać
s
, lvalue, toT&&
, kompilator wywnioskuje,T
że jeststd::string&
, dając argument typustd::string& &&
. Nie ma jednak odniesień do odniesień, więc to „podwójne odniesienie” zwija się i stajestd::string&
.s
pasuje.To proste wiążą się
7
zU&&
: kompilator może wywnioskowaćU
sięint
, uzyskując parametr typuint&&
, która wiąże się z powodzeniem7
, ponieważ jest RValue.Te nowe funkcje językowe mają wiele subtelności, ale jeśli zastosujesz się do jednej prostej zasady, będzie to całkiem proste:
źródło