Я создал простую функцию округления шаблона с дополнительным аргументом шаблона, который определяет тип, к которому необходимо привести округленное значение перед возвратом.
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 за ответ в мой другой вопрос.
Вы получаете ошибку, потому что возвращаемые типы не выводятся во время вывода аргумента шаблона. Вместо этого они подставляются из выведенных аргументов функции. Поскольку обе перегрузки имеют одинаковые аргументы, решение о перегрузке неоднозначно, что приводит к ошибке компилятора.
В 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>
(который, конечно, вы также можете использовать).
Заметка: временная переменная аналогична диспетчеризации тегов: она полностью используется для определения типа возвращаемого значения, а фактическое временное должно быть оптимизировано компилятором.
Ваша проблема не в специализации шаблонов, а в неоднозначности перегрузки.
Это похоже:
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();
}