Подтвердить что ты не робот

В std:: exchange, почему второй параметр шаблона дефолт?

В стандарте С++ 14 указано следующее объявление для std::exchange:

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

Мне интересно, почему U по умолчанию имеет значение T, так как U можно найти благодаря new_value. В каком случае это приведет к другому результату, чем:

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

Ответ 1

std::exchange был предложен в N3511 без аргумента шаблона по умолчанию, а позже N3608 с аргументом шаблона по умолчанию. Обратите внимание, что в N3608 были приведены следующие соображения:

Предоставление второго аргумента шаблона значение по умолчанию фиксирует после двух случаев:

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

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

Первый пример полезности - это, конечно, что нетипизированное временное {} будет выводиться на T. Второй пример более важен:

14.8.2 Вывод аргумента шаблона [temp.deduct]

5 Полученный замененный и скорректированный тип функции используется как тип шаблона функции для вывода аргумента шаблона. Если аргумент шаблона не был выведен и его соответствующий шаблон параметр имеет аргумент по умолчанию, аргумент шаблона определяется путем подстановки аргументов шаблона, определенных для предыдущих параметры шаблона в аргумент по умолчанию. Если подстановка приводит к недопустимому типу, как описано выше, вывод типа не выполняется.

14.8.2.5 Вывод шаблонных аргументов из типа [temp.deduct.type]

4 В большинстве случаев типы, шаблоны и значения не-типа, которые используемые для составления P, участвуют в выводе аргумента шаблона. То есть, они могут использоваться для определения значения аргумента шаблона и определяемое таким образом значение должно соответствовать установленным значениям в другом месте. Однако в некоторых контекстах ценность не участвовать в выводе типа, но вместо этого использует значения шаблона аргументы, которые были либо выведены в другом месте, либо явно указаны. Если параметр шаблона используется только в не выводимых контекстах и явно не указано, вывод аргумента шаблона не выполняется.

5 Невыводимые контексты:

(5.5) - Параметр функции, для которого аргументный вывод не может быть потому что связанный аргумент функции - это функция, или набор перегруженных функций (13.4) и один или несколько из следующих применить:

(5.5.1) - более одной функции соответствует параметру функции тип (что приводит к двусмысленному вычету)

Во втором примере параметр шаблона U используется только в невыводимом контексте, потому что две перегрузки f(int) и f(double) обе могут быть сопоставлены с U. Следовательно, вывод аргумента не имеет места, а U становится стандартным значением по умолчанию T (int (*)(int) в этом случае, поэтому выбирается f(int)).

Кроме того, как объясняется @VladfromMoscow, наличие параметра по умолчанию позволяет использовать более короткий код при передаче std::exchange<T> (к стандартному алгоритму, например).

Ответ 2

Функция может передаваться как аргумент какой-либо другой функции или, например, алгоритм. В этом случае достаточно указать только первый аргумент шаблона, если оба аргумента функции будут иметь один и тот же тип.

Это делает код более коротким и читаемым.

Вот искусственный пример.:)

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

Выходной сигнал

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

Или пример может включать преобразование

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