C ++ 11 make_pair z określonymi parametrami szablonu nie kompiluje się

84

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.
vmpstr
źródło
6
Świetne pytanie. Kolejny przykład subtelnej, przełomowej zmiany w C ++ 11, podobnej do przełomowej zmiany w std::vectorkonstrukcji . Przynajmniej ten powoduje błąd kompilatora, a nie cichą zmianę semantyki.
James McNellis,
1
Jeśli mam zmienną całkowitą i. Chcę sparować z i i innym obiektem. Jak dokładnie mam nazwać makepair. 1) make_pair <* i, obj> 2) int && j = i; make_pair <j, obj>? Obie nie działają. Jaki jest właściwy sposób, aby to zrobić?
PHcoDer,

Odpowiedzi:

135

To nie jest sposób, w jaki std::make_pairma być używany; nie powinieneś jawnie określać argumentów szablonu.

C ++ 11 std::make_pairprzyjmuje dwa argumenty typu T&&i U&&, gdzie Ti Usą 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_pairi 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. sjednak 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 Ti co Uznajduje 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 typu A&&, gdzie Ajest parametrem typu szablonu, może być powiązany z dowolnym rodzajem A.

Nie ma znaczenia, czy Ajest 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, gdy Asam jest parametrem szablonu).

W Twoim przykładzie dzwonimy:

make_pair(s, 7)

Tutaj sjest lwartość typu std::stringi 7jest rwartością typu int. 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, to T&&, kompilator wywnioskuje, Tże jest std::string&, dając argument typu std::string& &&. Nie ma jednak odniesień do odniesień, więc to „podwójne odniesienie” zwija się i staje std::string&. spasuje.

To proste wiążą się 7z U&&: kompilator może wywnioskować Usię int, uzyskując parametr typu int&&, która wiąże się z powodzeniem 7, 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:

Jeśli argument szablonu można wywnioskować z argumentów funkcji, niech zostanie wydedukowany. Nie podawaj jawnie argumentu, chyba że jest to absolutnie konieczne.

Pozwól kompilatorowi wykonać ciężką pracę, a przez 99,9% czasu będzie to dokładnie to, czego chciałeś. Jeśli nie jest to, czego chciałeś, zwykle pojawia się błąd kompilacji, który można łatwo zidentyfikować i naprawić.

James McNellis
źródło
6
To bardzo dobre i wyczerpujące wyjaśnienie. Dziękuję Ci!
vmpstr
1
@James - czy to „jedna prosta zasada” z innego artykułu lub odpowiedzi, którą powinienem przeczytać?
Michael Burr
4
@MichaelBurr: Nie, właśnie to wymyśliłem. :-) Mam nadzieję, że to prawda! Myślę, że to prawda ... ta zasada działa na mnie prawie cały czas.
James McNellis,
1
@James: dzięki. Otaczające go „cytat” sprawiło, że pomyślałem, że mogło to być coś, co zostało napisane gdzie indziej. Ta odpowiedź była naprawdę pouczająca i chciałem się tylko upewnić, że nie brakuje mi czegoś innego.
Michael Burr,
2
Czy dotyczy to również krotek?
Ferruccio