En std:: exchange, ¿por qué el segundo parámetro de plantilla está predeterminado?


El estándar C++14 especifica la siguiente declaración para std::exchange:

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

Me pregunto por qué U está por defecto en T ya que U se puede encontrar gracias a new_value. En qué caso, esto conduciría a un resultado diferente que:

template <class T, class U>
T std::exchange(T& obj, U&& new_value);
Author: TemplateRex, 2016-01-19

2 answers

El std::exchange fue propuesto en N3511 sin plantilla predeterminada argumento, y más tarde N3608 con una plantilla predeterminada argumento. Nótese que en N3608 se proporcionó el siguiente razonamiento:

Dar al segundo argumento de la plantilla un valor predeterminado soluciona la dos casos siguientes:

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

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

La utilidad del primer ejemplo es, por supuesto, que un temporal sin tipo {} se deducirá a T. El segundo ejemplo está más involucrado:

14.8.2 Deducción de argumento de plantilla [temp.deducir]

5 El tipo de función sustituida y ajustada resultante se utiliza como el tipo de la plantilla de función para deducción de argumento de plantilla. Si a no se ha deducido el argumento de la plantilla y su plantilla correspondiente el parámetro tiene un argumento predeterminado, el argumento de la plantilla está determinado sustituyendo los argumentos de plantilla determinados por el precedente parámetros de plantilla en el argumento predeterminado. Si la sustitución resulta en un tipo no válido, como se describió anteriormente, la deducción de tipo falla.

14.8.2.5 Deducir argumentos de plantilla de un tipo [temp.deducir.tipo]

4 En la mayoría de los casos, los tipos, plantillas y valores utilizado para componer P participar en la deducción de argumento de plantilla. Eso es, pueden utilizarse para determinar el valor de un argumento de plantilla, y el valor así determinado debe ser consistente con los valores determinados en otro lugar. En ciertos contextos, sin embargo, el valor no participar en la deducción de tipo, pero en su lugar utiliza los valores de plantilla argumentos que se dedujeron en otra parte o se especificaron explícitamente. Si un parámetro de plantilla se usa solo en contextos no deducidos y si no se especifica explícitamente, falla la deducción del argumento de plantilla.

5 Los contextos no deducidos son:

(5.5) - Un parámetro de función para el cual argumento la deducción no puede ser hecho porque el argumento de la función asociada es una función, o un conjunto de funciones sobrecargadas (13.4), y una o más de las siguientes aplicar:

(5.5.1) - más de una función coincide con el parámetro function type (resulting in an ambiguous deduction)

En el segundo ejemplo, el parámetro de plantilla U solo se usa en un contexto no deducido porque las dos sobrecargas f(int) y f(double) ambas pueden coincidir con U. Por lo tanto, la deducción de argumentos no tiene lugar, y U se convierte en el valor predeterminado suministrado de T (int (*)(int) en este caso, entonces se selecciona f(int)).

Además, como explica @VladfromMoscow, tener un parámetro predeterminado permite un código más corto al pasar std::exchange<T> (a un algoritmo estándar, por ejemplo).

 37
Author: TemplateRex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-19 21:57:22

La función se puede pasar como argumento a alguna otra función o por ejemplo algoritmo. En este caso es suficiente especificar solo el primer argumento de plantilla si ambos argumentos de la función tendrán el mismo tipo.

Esto hace que el código sea más corto y legible.

Aquí hay un ejemplo artificial.:)

#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 salida es

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

O el ejemplo podría incluir una conversión

#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;
}
 9
Author: Vlad from Moscow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-19 13:14:59