C ++ 11 make_pair con parametri di template specificati non si compila


85

Stavo solo giocando con g ++ 4.7 (una delle istantanee successive) con -std = c ++ 11 abilitato. Ho provato a compilare parte della mia base di codice esistente e un caso che non è riuscito in qualche modo mi confonde.

Apprezzerei se qualcuno potesse spiegare cosa sta succedendo.

Ecco il codice:

#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;
}

Capisco che make_pair sia pensato per essere usato come (1) caso (se specifico i tipi, allora potrei anche usare (3)), ma non capisco perché in questo caso non funziona.

L'errore esatto è:

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>&&’

Di nuovo, la domanda qui è solo "cosa sta succedendo?" So che posso risolvere il problema rimuovendo le specifiche del modello, ma voglio solo sapere cosa non funziona qui sotto le coperte.

  • g ++ 4.4 compila questo codice senza problemi.
  • La rimozione di -std = c ++ 11 compila anche il codice senza problemi.

6
Ottima domanda. Ancora un altro esempio di un sottile cambiamento di rottura in C ++ 11, simile al cambiamento di rottura nella std::vectorcostruzione . Almeno questo produce un errore del compilatore e non un cambiamento silenzioso nella semantica.
James McNellis

1
Se ho una variabile intera i. Voglio fare coppia con me e un altro oggetto. Come dovrei esattamente chiamare makepair. 1) make_pair <* i, obj> 2) int && j = i; make_pair <j, obj>? Entrambi non funzionano. Qual è il modo corretto per farlo?
PHcoDer

Risposte:


136

Questo non è il modo in cui std::make_pairdovrebbe essere utilizzato; non dovresti specificare esplicitamente gli argomenti del modello.

Il C ++ 11 std::make_pairaccetta due argomenti, di tipo T&&e U&&, dove Te Usono parametri del tipo di modello. In effetti, sembra così (ignorando il tipo di ritorno):

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

Quando chiami std::make_paire specifichi esplicitamente gli argomenti del tipo di modello, non viene eseguita alcuna detrazione di argomento. Invece, gli argomenti di tipo vengono sostituiti direttamente nella dichiarazione del modello, ottenendo:

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

Notare che entrambi questi tipi di parametri sono riferimenti rvalue. Pertanto, possono legarsi solo a rvalues. Questo non è un problema per il secondo argomento passato 7, perché si tratta di un'espressione rvalue. s, tuttavia, è un'espressione lvalue (non è temporanea e non viene spostata). Ciò significa che il modello di funzione non corrisponde ai tuoi argomenti, motivo per cui ricevi l'errore.

Quindi, perché funziona quando non si specifica esplicitamente cosa Te Usi trova nell'elenco degli argomenti del modello? In breve, i parametri di riferimento rvalue sono speciali nei modelli. A causa in parte di una funzionalità del linguaggio chiamata compressione dei riferimenti , un parametro di riferimento rvalue di tipo A&&, dove Aè un parametro di tipo di modello, può legarsi a qualsiasi tipo di A.

Non importa se Aè un lvalue, un rvalue, const-qualificato, volatile-qualificato o non qualificato, un A&&può legarsi a quell'oggetto (di nuovo, se e solo se Aè esso stesso un parametro del modello).

Nel tuo esempio, effettuiamo la chiamata:

make_pair(s, 7)

Qui sè un valore di tipo std::stringed 7è un valore di tipo int. Poiché non si specificano gli argomenti del modello per il modello della funzione, viene eseguita la deduzione dell'argomento del modello per capire quali sono gli argomenti.

Per associare s, un lvalue, a T&&, il compilatore deduce Tessere std::string&, restituendo un argomento di tipo std::string& &&. Non ci sono riferimenti a riferimenti, tuttavia, quindi questo "doppio riferimento" crolla per diventare std::string&. sè una partita.

È semplice da associare 7a U&&: il compilatore può dedurre Udi essere int, restituendo un parametro di tipo int&&, che si lega correttamente a 7perché è un valore.

Ci sono molte sottigliezze con queste nuove funzionalità del linguaggio, ma se segui una semplice regola, è abbastanza facile:

Se un argomento del modello può essere dedotto dagli argomenti della funzione, lascia che sia dedotto. Non fornire esplicitamente l'argomento a meno che non sia assolutamente necessario.

Lascia che sia il compilatore a fare il duro lavoro e il 99,9% delle volte sarà esattamente quello che volevi comunque. Quando non è quello che volevi, di solito ricevi un errore di compilazione che è facile da identificare e correggere.


6
Questa è una spiegazione molto buona e completa. Grazie!
vmpstr

1
@ James - è "una semplice regola" tratta da un altro articolo o risposta che dovrei leggere?
Michael Burr

4
@MichaelBurr: Nah, l'ho appena inventato. :-) Quindi, spero sia vero! Penso che sia vero ... quella regola funziona per me praticamente tutto il tempo.
James McNellis

1
@ James: grazie. La "casella delle virgolette" intorno mi ha fatto pensare che potesse essere stato scritto originariamente altrove. Questa risposta è stata davvero istruttiva e volevo solo assicurarmi di non perdere qualcosa da qualche altra parte.
Michael Burr

2
Questo vale anche per le tuple?
Ferruccio
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.