36 votes

Dans std :: exchange, pourquoi le deuxième paramètre de modèle est-il par défaut?

La norme C ++14 spécifie la déclaration suivante pour std::exchange :

 template <class T, class U = T>
T std::exchange(T& obj, U&& new_value);
 

Je me demande pourquoi U est réglé par défaut sur T car U peut être trouvé grâce à new_value . Dans ce cas, cela conduirait à un résultat différent de:

 template <class T, class U>
T std::exchange(T& obj, U&& new_value);
 

39voto

TemplateRex Points 26447

L' std::exchange a été proposé dans N3511 sans modèle par défaut de l'argument, et plus tard N3608 avec un modèle par défaut de l'argument. Notez que dans N3608 le raisonnement suivant a été fourni:

Donner le deuxième argument de modèle, par défaut, une valeur fixe à la deux cas suivants:

DefaultConstructible x = ...;
if (exchange(x, {})) { ... }

int (*fp)(int);
int f(int);
double f(double);
/*...*/ exchange(fp, &f) /*...*/

Le premier exemple de l'utilité est bien sûr qu'un non temporaire {} sera déduite pour T. Le deuxième exemple est plus complexe:

14.8.2 argument de Modèle déduction [temp.déduire]

5 L'résultant substitués et ajustés type de fonction est utilisé comme le type de la fonction de modèle de modèle à l'argument de la déduction. Si un modèle argument n'a pas été déduit et son modèle correspondant le paramètre par défaut de l'argument, l'argument de modèle est déterminé en remplaçant les arguments de modèle déterminé pour les précédentes les paramètres de modèle dans l'argument par défaut. Si la substitution résultats dans un type non valide, comme décrit ci-dessus, le type de déduction échoue.

14.8.2.5 en Déduire des arguments de modèle à partir d'un type [temp.déduire.type]

4 Dans la plupart des cas, les types, les modèles et les non-type des valeurs qui sont utilisé pour composer P participer à l'argument de modèle de déduction. Qui est, ils peuvent être utilisés pour déterminer la valeur d'un argument de modèle, et la valeur ainsi déterminée doit être cohérente avec les valeurs déterminées d'ailleurs. Dans certains contextes, cependant, la valeur n'est pas participer à type de déduction, mais utilise à la place les valeurs de modèle des arguments qui ont été déduites ou ailleurs explicitement spécifié. Si un paramètre du modèle est utilisé uniquement dans les non déduit des contextes et de l'est pas explicitement spécifié, argument de modèle déduction échoue.

5 La non-déduire les contextes sont:

(5.5) - paramètre d'Une fonction dont l'argument de la déduction ne peut pas être fait parce que la fonction associée argument est une fonction, ou un ensemble de surcharge de fonctions (13.4), et un ou plusieurs des éléments suivants s'appliquent:

(5.5.1) - plus d'une fonction correspond au paramètre de la fonction type (résultant en une ambigu déduction)

Dans le deuxième exemple, le paramètre de modèle U n'est utilisé que dans un non déduit contexte, car les deux surcharges f(int) et f(double) à la fois peut être jumelé à U. Par conséquent, l'argument de la déduction n'a pas lieu, et U devient la valeur par défaut fourni la valeur de T (int (*)(int) dans ce cas, f(int) est sélectionné).

En outre, comme expliqué par @VladfromMoscow, ayant un défaut paramètre permet pour de courtes code lors du passage d' std::exchange<T> (à un algorithme standard par exemple).

10voto

Vlad from Moscow Points 36219

La fonction peut être passée en argument à une autre fonction ou par exemple à un algorithme. Dans ce cas, il suffit de spécifier uniquement le premier argument de modèle si les deux arguments de la fonction auront le même type.

Cela rend le code plus court et plus lisible.

Voici un exemple artificiel. :)

 #include <iostream>
#include <numeric>
#include <iterator> 
#include <functional>


int main()
{
    int a[] = { 1, 2, 3 };
    int b[] = { 4, 5, 6 };

    std::cout << "a: ";
    for ( int x : a ) std::cout << x << ' ';
    std::cout << std::endl;
    std::cout << "b: ";
    for ( int x : b ) std::cout << x << ' ';
    std::cout << std::endl;

    auto sum = std::inner_product( std::begin( a ), std::end( a ),
                                   std::make_move_iterator( std::begin( b ) ), 0,
                                   std::plus<int>(), std::exchange<int> );

    std::cout << "sum = " << sum << std::endl;
    std::cout << "a: ";
    for ( int x : a ) std::cout << x << ' ';
    std::cout << std::endl;
}
 

La sortie est

 a: 1 2 3 
b: 4 5 6 
sum = 6
a: 4 5 6 
 

Ou l'exemple pourrait inclure une conversion

 #include <iostream>
#include <numeric>
#include <iterator> 
#include <functional>


int main()
{
    int a[] = { 1, 2, 3 };
    double b[] = { 4.4, 5.5, 6.6 };

    std::cout << "a: ";
    for ( int x : a ) std::cout << x << ' ';
    std::cout << std::endl;
    std::cout << "b: ";
    for ( double x : b ) std::cout << x << ' ';
    std::cout << std::endl;

    auto sum = std::inner_product( std::begin( a ), std::end( a ),
                                   std::make_move_iterator( std::begin( b ) ), 0,
                                   std::plus<>(), std::exchange<int> );

    std::cout << "sum = " << sum << std::endl;
    std::cout << "a: ";
    for ( int x : a ) std::cout << x << ' ';
    std::cout << std::endl;
}
 

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