5 votes

Pourquoi les tableaux de const se lient-ils préférentiellement aux paramètres const T& plutôt qu'aux paramètres T&& ?

C'est généralement une mauvaise idée de surcharger un modèle de fonction prenant un paramètre "T&&", car il peut se lier à n'importe quoi, mais supposons que nous le fassions quand même :

template<typename T>
void func(const T& param)
{
  std::cout << "const T&\n";
}

template<typename T>
void func(T&& param)
{
  std::cout << "T&&\n";
}

D'après ce que j'ai compris, le const T& sera appelée pour les arguments qui sont des valeurs constantes, et la surcharge T&& sera appelée pour tous les autres types d'arguments. Mais considérez ce qui se passe lorsque nous appelons func avec des tableaux de contenus constants et non constants :

int main()
{
  int array[5] = {};
  const int constArray[5] = {};

  func(array);             // calls T&& overload
  func(constArray);        // calls const T& overload
}

VC10, VC11, et gcc 4.7 sont en accord avec les résultats indiqués. Ma question est de savoir pourquoi le deuxième appel invoque la fonction const T& surcharge. La réponse simple est que constArray a un const dedans, mais je pense que c'est trop simple. Le type T qui est déduit (indépendamment du modèle sélectionné) est "array of 5 const ints", donc le type de param dans le const T& La surcharge serait "référence à un tableau constant de 5 const ints". Mais le tableau nommé constArray n'est pas lui-même déclaré const. Alors pourquoi l'appel à func(constArray) invoquer le T&& ce qui permet d'obtenir un type pour param de "référence à un tableau de 5 const ints" ?

Cette question est motivée par la discussion associée à la question à l'adresse suivante c++ template function argument deduce and function resolution mais je pense que ce fil de discussion a été détourné sur d'autres sujets et n'a pas clarifié la question que je pose maintenant ici.

7voto

ecatmur Points 64173

Dans les listes de paramètres de fonctions (ainsi que partout ailleurs), une qualification cv sur un type de tableau est mélangée à droite pour qualifier le type d'élément du tableau. Par exemple, avec T = int [5] , const T & est converti en int const (&) [5] .

3.9.3 CV-qualiers [basic.type.qualier]

2 - [...] Tout qualificatif cv appliqué à un type de tableau affecte le type de l'élément du tableau, pas le type du tableau (8.3.4).

Donc l'appel à func avec un argument de type int const [5] est déduit comme un appel à l'un ou l'autre de :

void func<int [5]>(int const (&) [5])
void func<int const (&) [5]>(int const (& &&) [5])
// where the above collapses to
// 'void func<int const (&) [5]>(int const (&) [5])'

Les deux surcharges sont viables, mais la première est préférable :

Soit T1 le const T & et T2 est le modèle T && c'est-à-dire que leurs types de paramètres sont T1 := const T & et T2 := T && . Alors les types d'arguments transformés (14.5.6.2:3) peuvent être écrits A1 := const C & , A2 := D && pour les types synthétisés C , D .

Maintenant, nous essayons d'ordonner T1 contre T2 (14.8.2.4:2), en utilisant d'abord A1 comme modèle d'argument et P2 comme modèle de paramètre. Nous supprimons les références (14.8.2.4:5) donnant A1 -> const C et T2 -> T , puis supprimer la qualification cv (14.8.2.4:7), ce qui donne A1 -> C et T2 -> T . Le modèle T peut être déduit de C (14.8.2.4:8) donc A1 est au moins aussi spécialisé que P2 ; inversement, A2 -> D -> D , P1 -> const T -> T y T peut être déduit de D donc A2 est au moins aussi spécialisé que P1 .

Cela implique généralement qu'aucun des deux n'est plus spécialisé que l'autre. P y A sont des types de référence 14.8.2.4:9 s'applique, et puisque A1 est une référence lvalue et P2 ne l'est pas, T1 est considéré comme plus spécialisé que T2. (Une égalité entre types de référence peut également être rompue par cv-qualification sous la même clause).

3voto

Kerrek SB Points 194696

Vous confondez les références rvalue (comme int&& ) et des références universelles (qui sont faites à partir de paramètres de modèles, comme dans template <typename T> ... T&& ).

Les références aux valeurs R ne sont en effet pas liées aux valeurs l. Mais les références universelles se lient à n'importe quoi. La question est de savoir qui correspond le mieux.

Le type que vous avez est int const [5] . Maintenant, voyons :

  • contre T const & : Correspond à T = int[5] .

  • contre T && : Correspond à T = int const (&)[5] .

Le premier correspond mieux, dans le sens suivant : les deux modèles produisent des surcharges identiques. Mais T = int[5] es plus spécialisé que T = int const (&)[5] . Vous pouvez le constater car T = int const (&)[5] peut être réalisé comme suit T = U const & avec U = int[5] .

Notez que pour lier une lvalue à une référence universelle, le type lui-même doit être déduit comme un type de référence.

(Évidemment array ne correspond pas const T & parce qu'il n'est pas constant. Il peut seulement correspondre à T&& en déduisant T = int (&)[5] ).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X