Domanda In std :: exchange, perché il parametro del secondo template è default?


Lo standard C ++ 14 specifica la seguente dichiarazione per std::exchange:

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

Mi chiedo perché U è predefinito a T da U può essere trovato grazie a new_value. In tal caso, ciò porterebbe a un risultato diverso rispetto a:

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

33
2018-01-19 12:34


origine


risposte:


Il std::exchange è stato proposto a N3511  senza argomento del template di default, e più tardi N3608 con un argomento modello predefinito. Si noti che in N3608 è stato fornito il seguente ragionamento:

Dando il secondo argomento template un valore predefinito corregge il   seguendo due casi:

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

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

L'utilità del primo esempio è ovviamente un temporaneo non tipizzato {} sarà dedotto a T. Il secondo esempio è più coinvolto:

14.8.2 Detrazione argomento template [temp.deduct]

5 Il tipo di funzione sostituita e aggiustata risultante viene utilizzato come   tipo del modello di funzione per deduzione argomento modello. Se un   argomento modello non è stato dedotto e il suo modello corrispondente   parametro ha un argomento predefinito, l'argomento del template è determinato   sostituendo gli argomenti del modello determinati per precedente   parametri del modello nell'argomento predefinito. Se la sostituzione   restituisce un tipo non valido, come descritto sopra, la deduzione di tipo non riesce.

14.8.2.5 Dedurre argomenti template da un tipo [temp.deduct.type]

4 Nella maggior parte dei casi, i tipi, i modelli e i valori non di tipo che sono   usato per comporre P partecipa alla deduzione argomento template. Questo è,   possono essere usati per determinare il valore di un argomento modello, e   il valore così determinato deve essere coerente con i valori determinati   altrove. In alcuni contesti, tuttavia, il valore non lo è   partecipare alla deduzione di tipo, ma utilizza invece i valori del modello   argomenti che sono stati dedotti altrove o esplicitamente specificati.    Se un parametro template viene usato solo in contesti non dedotti e is   non specificato esplicitamente, la deduzione dell'argomento modello non riesce.

5 I contesti non dedotti sono:

(5.5) - Un parametro di funzione per il quale la deduzione argomento non può essere   fatto perché l'argomento della funzione associata è una funzione, o un set   di funzioni sovraccariche (13.4) e uno o più dei seguenti   applicare:

(5.5.1) - più di una funzione corrisponde al parametro della funzione   genere (risultante in una deduzione ambigua)

Nel secondo esempio, il parametro template U è usato solo in un contesto non dedotto perché i due sovraccarichi f(int) e f(double) entrambi possono essere abbinati a U. Quindi, la deduzione argomento non ha luogo, e U diventa il valore predefinito fornito di T (int (*)(int) in questo caso, così f(int) è selezionato).

Inoltre, come spiegato da @VladfromMoscow, avere un parametro predefinito consente di inserire un codice più corto durante il passaggio std::exchange<T> (ad un algoritmo standard, ad es.).


36
2018-01-19 12:43



La funzione può essere passata come argomento ad un'altra funzione o ad esempio all'algoritmo. In questo caso è sufficiente specificare solo il primo argomento del template se entrambi gli argomenti della funzione avranno lo stesso tipo.

Ciò rende il codice più breve e leggibile.

Ecco un esempio artificiale. :)

#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;
}

L'output è

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

Oppure l'esempio potrebbe includere una conversione

#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
2018-01-19 12:39