c++ - En std:: exchange, ¿por qué el segundo parámetro de la plantilla está predeterminado?
templates stl (2)
La función se puede pasar como argumento a alguna otra función o, por ejemplo, algoritmo. En este caso, basta con especificar solo el primer argumento de la 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;
}
El resultado 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;
}
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á predeterminado a T
ya que U
se puede encontrar gracias a new_value
. En tal caso, esto daría lugar a un resultado diferente de:
template <class T, class U>
T std::exchange(T& obj, U&& new_value);
std::exchange
se propuso en N3511 sin argumento de plantilla predeterminado, y luego N3608 con un argumento de plantilla predeterminado. Tenga en cuenta que en N3608 se proporcionó el siguiente razonamiento:
Dar al segundo argumento de plantilla un valor predeterminado corrige los 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 deduzca a T
El segundo ejemplo es más complicado:
14.8.2 Deducción del argumento de la plantilla [temp.deduct]
5 El tipo de función sustituida y ajustada resultante se utiliza como el tipo de plantilla de función para la deducción de argumento de plantilla. Si no se deduce un argumento de plantilla y su parámetro de plantilla correspondiente tiene un argumento predeterminado, el argumento de plantilla se determina sustituyendo los argumentos de plantilla determinados para los parámetros de plantilla anteriores en el argumento predeterminado. Si la sustitución da como resultado un tipo no válido, como se describió anteriormente, el tipo deducción falla.
14.8.2.5 Deducir argumentos de plantilla de un tipo [temp.deduct.type]
4 En la mayoría de los casos, los tipos, plantillas y valores sin tipo que se usan para componer P participan en la deducción del argumento de la plantilla. Es decir, pueden usarse para determinar el valor de un argumento de plantilla, y el valor así determinado debe ser consistente con los valores determinados en otra parte. Sin embargo, en ciertos contextos, el valor no participa en la deducción de tipo, sino que utiliza los valores de los argumentos de plantilla que se dedujeron en otro lugar o se especificaron explícitamente. Si un parámetro de plantilla se usa solo en contextos no deducidos y no se especifica explícitamente, la deducción de argumentos de la plantilla falla.
5 Los contextos no deducidos son:
(5.5) - Un parámetro de función para el que no se puede hacer una deducción de argumento porque el argumento de función asociado es una función, o un conjunto de funciones sobrecargadas (13.4), y uno o más de los siguientes:
(5.5.1) - más de una función coincide con el tipo de parámetro de función (lo que resulta en una deducción ambigua)
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 argumento no tiene lugar, y U
convierte en el valor predeterminado de T
( int (*)(int)
en este caso, por lo que se selecciona f(int)
).
Además, como se explica en @VladfromMoscow, tener un parámetro predeterminado permite código más corto al pasar std::exchange<T>
(a un algoritmo estándar, por ejemplo).