Il codice secondo esempio non sarebbe compilare, che è un sintomo della differenza tra la risoluzione di sovraccarico fase di compilazione e fase di esecuzione condizionale ramificazione a "scegliere" quale codice da eseguire.
- "Il sovraccarico di una funzione di prendere
true_type
o false_type
parametro" consente di fare questa scelta al momento della compilazione se la decisione dipende solo tipi e compilare in tempo costanti.
- "Uso di un controllo
if
" è necessaria se la scelta non può essere fatto fino al momento esecuzione quando sono note alcune valori delle variabili.
Nel tuo esempio lo bool match = my_list.contains(my_value)
non è ovviamente noto prima di eseguire il programma, quindi non è possibile utilizzare l'overloading.
Ma la differenza è più importante per i modelli , in cui la scelta non è solo "quale percorso da eseguire", ma "quale codice per instantiate e compilare e quindi chiamare". Il codice dal vostro linked video è piuttosto in questo spirito:
Considerate questo codice (sbagliato) (omettendo #include
s e std::
s per brevità):
template<typename InIt>
typename iterator_traits<InIt>::difference_type
distance(InIt first, InIt last)
{
// Make code shorter
typedef typename iterator_traits<InIt>::difference_type Diff;
typedef typename iterator_traits<InIt>::iterator_category Tag;
// Choice
if (is_same<Tag, random_access_iterator_tag>::value)
{
return last - first;
}
else
{
Diff n = 0;
while (first != last) {
++first;
++n;
}
return n;
}
}
Ci sono almeno due problemi qui:
- Se si tenta di chiamare con iteratori che sono non ad accesso casuale (ad esempio
std::list<T>::iterator
), sarà effettivamente riuscire a compilare (con un errore che indica la linea return last - first;
). Il compilatore deve creare un'istanza e compilare l'intero corpo della funzione, tra cui sia il if
e le filiali else
(anche se deve essere eseguito solo uno) e l'espressione last - first
non è valida per gli iteratori non RA.
- Anche se quel compilato, avremmo fatto un test a fase di esecuzione (con il relativo sovraccarico) per una condizione che avremmo potuto provato appena tempo di compilazione, e la compilazione di parti di codice non necessarie. (Il compilatore può essere in grado di ottimizzare quello, ma questo è il concetto.)
per risolvere il problema che si può fare:
// (assume needed declarations...)
template<typename InIt>
typename iterator_traits<InIt>::difference_type
distance(InIt first, InIt last)
{
// Make code shorter
typedef typename iterator_traits<InIt>::iterator_category Tag;
// Choice
return distanceImpl(first, last, is_same<Tag, random_access_iterator_tag>());
}
template<typename InIt>
typename iterator_traits<InIt>::difference_type
distanceImpl(InIt first, InIt last, true_type)
{
return last - first;
}
template<typename InIt>
typename iterator_traits<InIt>::difference_type
distanceImpl(InIt first, InIt last, false_type)
{
// Make code shorter
typedef typename iterator_traits<InIt>::difference_type Diff;
Diff n = 0;
while (first != last) {
++first;
++n;
}
return n;
}
o, in alternativa (possibile qui) con i tipi direttamente:
/* snip */
distance(InIt first, InIt last)
{
/* snip */
return distanceImpl(first, last, Tag());
}
/* snip */
distanceImpl(InIt first, InIt last, random_access_iterator_tag)
{
return last - first;
}
/* snip */
distanceImpl(InIt first, InIt last, input_iterator_tag)
{
/* snip */
Diff n = 0;
/* snip */
return n;
}
Ora solo il "corretto" distanceImpl
verrà istanziato e chiamato (la scelta viene eseguita in fase di compilazione).
Questo funziona perché i tipi (ad esempio InIt
o Tag
) sono noti in fase di compilazione e is_same<Tag, random_access_iterator_tag>::value
è una costante che è nota anche in fase di compilazione. Il compilatore può risolvere quale sovraccarico deve essere chiamato, solo in base ai tipi (è la risoluzione di sovraccarico).
Nota: anche se i "tag" vengono passati per valore, vengono utilizzati solo come parametri non utilizzati e non utilizzati per "dispatch" (il loro valore non è utilizzato, solo il loro tipo) e il compilatore può ottimizzarli.
È anche possibile leggere Elemento 47: Utilizzare le classi di tratti per informazioni sui tipi da Scott Meyers Effective C++, Third Edition.
Ci possono essere dei motivi per farlo nelle funzioni 'constexpr' che sono usate in fase di compilazione. Gli esempi che hai trovato sono stati compilati? (Questo non funzionerebbe con l'output su STDOUT, ovviamente.) – jogojapan
@jogojapan L'ho visto essere usato decine di volte, ma l'ultimo era nella funzione 'find' della libreria stl' algorithm', come visto su [questo video] (http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/Stephan-T-Lavavej-Core-C-7-of-n) alle 15 circa: 00. Sono abbastanza sicuro che sia usato al momento della compilazione. –