Частичная специализация шаблона при перегрузке

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

template <typename T, typename U>
T round(U val) {
T result;
if (val >= 0)
result = (T)(floor(val + (U)(.5)));
else
result = (T)(ceil( val - (U)(.5)));
return result;
}

int a = round<int>(5.5); // = 6
// no compiler warnings

Но я также хочу иметь возможность оставить дополнительный аргумент шаблона, чтобы вам не приходилось вставлять тип, который вы уже добавили в качестве аргумента.

template <typename T>
T round(T val) {
return round<T>(val);
}

double b = round(5.5) // = 6.0
// C2668

Однако теперь компилятор жалуется:

ошибка C2668: неоднозначный вызов перегруженной функции

Я думал, что компилятор всегда будет выбирать наиболее конкретный шаблон, который должен быть последним. Почему это не так, и есть ли обходные пути (не специально для этой функции раунда)?


Неоднозначный вызов не указывал на round(5.5) а скорее return round<T>(val);, Таким образом, ответом на этот вопрос было переписать возвращаемое значение для перегруженной функции в

return round<T,T>(val);

которая решает проблему.

Благодаря galop1n за ответ в мой другой вопрос.

0

Решение

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

В C ++ 11 вы можете определить параметр шаблона по умолчанию для шаблонов функций. Если вы добавите дополнительный параметр функции по умолчанию, равный возвращаемому значению по умолчанию, вы всегда получите тип аргумента в качестве возвращаемого, если вы явно не передадите возвращаемое значение по умолчанию:

#include <iostream>
#include <cmath>
#include <type_traits>

template <typename T, typename Ret = T>
Ret xround(T val, Ret ret = Ret()) {
return static_cast<Ret>(
(val >= 0) ?
floor(val + (T)(.5)) :
ceil( val - (T)(.5))
);
}

int main()
{
auto a = xround(5.5, int()); // = 6
static_assert(std::is_same<decltype(a), int>::value, "");
std::cout << a << "\n";

auto b = xround(5.5); // = 6.0
static_assert(std::is_same<decltype(b), double>::value, "");
std::cout << b << "\n";
}

Живой пример

Обратите внимание, что я использовал троичный оператор вместо вашего if-elseи что я переименовал функцию в xround потому что в C ++ 11 уже есть round внутри <cmath> (который, конечно, вы также можете использовать).

Заметка: временная переменная аналогична диспетчеризации тегов: она полностью используется для определения типа возвращаемого значения, а фактическое временное должно быть оптимизировано компилятором.

2

Другие решения

Ваша проблема не в специализации шаблонов, а в неоднозначности перегрузки.

Это похоже:

int fn(int) { return 0; }
// error: new declaration ‘double fn(int)’
// error: ambiguates old declaration ‘int fn(int)’
double fn(int) { return 0; }

Наличие шаблона, где U имеет T в качестве параметра по умолчанию, не будет лучше:

template <typename T, typename U = T>
T fn(U val) {
return T();
}

int main() {
// error: no matching function for call to ‘fn(double)’
// note: template argument deduction/substitution failed:
double d = fn(1.5); // fn<double>(1.5) will work
}

И частичная специализация не допускается:

template <typename T, typename U>
T fn(U val) {
return T();
}

// error: function template partial specialization ‘fn<T, T>’ is not allowed
template <typename T>
T fn<T, T>(T val) {
return T();
}
0