Così qui è quello che succede (o meglio, dovrebbe succedere): al fine di risolvere questo chiamata al costruttore
Myclass b(a);
Il compilatore deve eseguire la risoluzione di sovraccarico e decidere, in un primo momento, quali costruttori sono candidati validi.
La prima cosa da notare è che entrambi i costruttori sono vitali: forme come T&&
fanno non risolvere sempre in riferimenti rvalue (che è solo il caso se ciò che si sta passando è un rvalue). Questo è ciò che Scott Meyers chiama "universal references" (nota che questo termine non è standard).
Quando il compilatore tenta di eseguire il tipo detrazione per vedere se il secondo costruttore è vitale, il tipo T
in questo caso sarà dedotto di essere Myclass&
- poiché ciò che si sta passando (a
) è un lvalue; e a causa delle regole di compressione di riferimento, Myclass& &&
fornisce Myclass&
, quindi si ottiene la stessa firma del primo costruttore.
Quindi la chiamata è ambigua? Come sottolineato da Marc Glisse in the comments to the question, e da Jonathan Wakely in the comments to this answer, no, non dovrebbe essere (come ha sostenuto la versione originale di questa risposta - mea culpa).
Il motivo è che una regola speciale nello standard specifica che il sovraccarico che accetta un riferimento di lvalue è più specializzato rispetto al sovraccarico che accetta un riferimento di rvalue. Al punto 14.8.2.4/9 del C++ 11 standard:
Se, per un dato tipo, deduzione riesce in entrambe le direzioni (cioè, i tipi sono identici dopo le trasformazioni sopra) e sia P e a fosse tipi di riferimento (prima di essere sostituito con il tipo di cui sopra):
- se il tipo dal modello argomento era un riferimento Ivalue e il tipo dal modello parametro non è stato, il tipo di argomento è considerato per essere più specializzati rispetto agli altri; in caso contrario, [...]
Questo significa che il compilatore ha un bug (the link to the bug report è stato fornito da Marc Glisse nei commenti alla domanda).
per risolvere questo bug e assicurarsi che il modello costruttore di accettare un T&&
sarà scelto da GCC solo quando vengono passati rvalues, è possibile riscrivere in questo modo:
#include <type_traits>
template<typename U,
typename std::enable_if<
!std::is_reference<U>::value
>::type* = nullptr>
Myclass(U&& rvalue):i(rvalue)
{cout<<i <<" template right reference" <<endl;i++;}
Dove ho aggiunto uno SFINAE-constraint il che rende il compilatore scartare questo costruttore dal set di overload quando viene passato un lvalue.
Quando viene passato un valore assegnabile, infatti, T
sarà dedotto di essere X&
per qualche X
(il tipo di espressione si passa, Myclass
nel tuo caso), e T&&
sarà risolvere in X&
; quando viene passato un valore, d'altra parte, viene dedotto da da X
(il tipo dell'espressione che si passa, Myclass
nel tuo caso) e T&&
verrà risolto in X&&
.
Dal momento che i controlli SFINAE vincolo siano essi T
non si deduce di essere un tipo di riferimento e crea un fallimento di sostituzione in caso contrario, il vostro costruttore è garantito per essere preso in considerazione solo quando l'argomento è un'espressione rvalue.
Quindi, per riassumere il tutto:
#include <iostream>
#include <type_traits>
class Myclass
{
int i;
public:
template<typename U>
Myclass(U& lvalue):i(lvalue)
{
std::cout << i <<" template light reference" << std::endl;
i++;
}
template<typename U,
typename std::enable_if<
!std::is_reference<U>::value
>::type* = nullptr>
Myclass(U&& rvalue):i(rvalue)
{
std::cout << i <<" template right reference" << std::endl;
i++;
}
};
int main(int argc,char*argv[])
{
Myclass a(0);
int x = 42;
Myclass b(x);
Myclass c(2);
}
Ecco un live example.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57172 –
@MarkTolonen cambiando a 'const U &' risulta l'output di 'modello di riferimento a destra' – yuan
Il bug è stato corretto per 4.9, sentiti libero giocarci e segnalare eventuali problemi riscontrati con il nuovo comportamento. –