Récemment, j'ai constaté que GCC
a modifié le comportement lors de la commande partielle, le cas concret est le suivant :
#include <iostream>
template<class T>
struct unknow_context{
using type = int;
};
template<class U>
void show(typename unknow_context<U>::type, U){ // candidate #1
std::cout<<"#1\n";
}
template<class T>
void show(int, T){ // candidate #2
std::cout<<"#2\n";
}
int main(){
show(0,0);
}
Le résultat est le suivant, Clang empreintes #2
(toute version de Clang
a imprimé un résultat cohérent). Cependant, GCC
a un comportement différent. L'ancienne version de CCG empreintes #2
Au lieu de cela, la dernière CCG se plaint de l'ambiguïté des fonctions des candidats. Voyons ce que dit la norme à propos de l'ordre partiel.
temp.deduct.partial#2
Le processus de déduction utilise le type transformé comme modèle d'argument et le type original de l'autre modèle comme modèle de paramètre. Ce processus est effectué deux fois pour chaque type impliqué dans la comparaison de l'ordre partiel : une fois en utilisant le modèle-1 transformé comme modèle argument et le modèle-2 comme modèle paramètre et une autre fois en utilisant le modèle-2 transformé comme modèle argument et le modèle-1 comme modèle paramètre.
Nous pouvons donc obtenir deux séries de paires P/A pour le candidat #1
et #2
respectivement. L'un d'entre eux est le #1
comme A, l'original #2
comme P. L'autre est l'original #1
comme P, la valeur transformée #2
comme A. Les deux ensembles seront donc donnés comme suit :
#a
|--------|------------------------------------------|
| P (#2) | A (#1) |
|--------|------------------------------------------|
| int | typename unknow_context<UniqueA>::type |
|--------|------------------------------------------|
| T | UniqueB |
|--------|------------------------------------------|
T
peut être déduit de UniqueB
. Pour la première série, la règle dit :
Si un P particulier ne contient aucun paramètre de modèle qui participe à la déduction des arguments de modèle, ce P n'est pas utilisé pour déterminer l'ordre.
Ainsi, nous les mettons d'abord de côté et les examinons plus tard .
#b
|----------------------------------|-------|
|P (#1) |A (#2) |
|----------------------------------|-------|
| typename unknow_context<U>::type |int |
|----------------------------------|-------|
| U |UniqueA|
|----------------------------------|-------|
Pour un contexte non éduqué, sa valeur peut être obtenue ailleurs.
Dans certains contextes, cependant, la valeur ne participe pas à la déduction de type, mais utilise les valeurs des arguments du modèle qui ont été soit déduites ailleurs, soit spécifiées explicitement. Si un paramètre de modèle n'est utilisé que dans des contextes non déduits et n'est pas explicitement spécifié, la déduction des arguments de modèle échoue.
Nous pouvons donc ignorer la paire typename unknow_context<U>::type
/ int
et de considérer U
/ UniqueA
. U
peut être déduit de UniqueA
.
Si la déduction réussit pour un type donné, le type du modèle d'argument est considéré comme étant au moins aussi spécialisé que le type du modèle de paramètre.
De #b
on obtient le résultat suivant #2
est au moins aussi spécialisé que #1
.
La question se pose dans la #a set
. La deuxième paire ne pose pas de problème, on peut dire que la deuxième partie de #1
est au moins aussi spécialisée que la deuxième partie de la #2
.
Toutefois, la règle permettant de déterminer si un modèle de fonction F est plus spécialisé qu'un modèle de fonction G est définie comme suit :
Le modèle de fonction F est au moins aussi spécialisé que le modèle de fonction G si, pour pour chaque paire de types utilisés pour déterminer l'ordre, le type de F est au moins aussi spécialisé que le type de G. F est plus spécialisé que G si F est au moins aussi spécialisé que G et si G n'est pas au moins aussi spécialisé que F. .
Nous veillons donc à former des paires int / typename unknow_context<UniqueA>::type
Bien que la règle précise que "P n'est pas utilisé pour déterminer l'ordre". Cependant, une note importante de la norme précise que :
[Note : Sous [temp.deduct.call] et [temp.deduct.partial], si P ne contient aucun paramètre de modèle apparaissant dans des contextes déduits, aucune déduction n'est effectuée, donc P et A n'ont pas nécessairement la même forme . -note de fin]
Ainsi, dès aujourd'hui, à partir de la P/A set #a
, #1
est toujours au moins aussi spécialisé que #2
(succès de la déduction). Je pense donc que la dernière GCC
devrait être correcte (ambiguë, aucune des deux n'est plus spécialisée que l'autre).
Question 1 :
Quel est le bon compilateur ?
Question 2 :
l'instanciation de la spécialisation doit-elle être effectuée lors de la commande partielle ? La norme ne semble pas préciser si l'instanciation sera effectuée.
#include <iostream>
template<class T>
struct unknow_context{
using type = T;
};
template<class U>
void show(typename unknow_context<U>::type, U){
std::cout<<"#1\n";
}
template<class T>
void show(T, T){
std::cout<<"#2\n";
}
int main(){
show(0,0);
}
Tous ont choisi #2
. Je suis préoccupé par les P/A pair
c'est à dire :
|----|------------------------------------------------------------|
|P |A |
|----|------------------------------------------------------------|
|T |typename unknow_context<UniqueA>::type /*Is it equivalent to|
| | UniqueA? */ |
|----|------------------------------------------------------------|
|T |UniqueA |
|----|------------------------------------------------------------|
si typename unknow_context<UniqueA>::type
sera calculée pour UniqueA
? Il semble que le compilateur traite typename unknow_context<UniqueA>::type
en tant que type unique plutôt que de le calculer en UniqueA
.