Partial template specialization by overloading

青春壹個敷衍的年華 提交于 2019-12-13 06:26:20

问题


I created a simple round template function with an extra template argument that defines the type the rounded value needs to be casted to before returning.

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

But I also want the possibility to leave the extra template argument so that you don't have to insert the type you already added as an argument.

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

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

However, now the compiler complains:

error C2668: ambiguous call to overloaded function

I thought the compiler would always choose the most specific template, which should be the last one. Why is this not the case and are there any workarounds (not specifically for this round function)?


The ambiguous call wasn't pointing to round(5.5) but rather to return round<T>(val);. The answer to this question was thus to rewrite the return value for the overloaded function to

return round<T,T>(val);

which solves the problem.

Thanks to galop1n for the answer to my other question.


回答1:


You are getting an error because return types are not deduced during template argument deduction. Instead they are substituted from the deduced function arguments. Because both overloads have the same arguments deduced, overload resoution is ambiguous which gives a compiler error.

In C++11 you can define a default template parameter for function templates. If you add an extra default function parameter equal to the default return value, you will always get the argument type as return type, unless you explicitly pass a default return value:

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

Live Example

Note that I used the ternary operator instead of your if-else, and that I renamed the function to xround because in C++11 there already is a round inside <cmath> (which of course you could also use).

Note: the temporary is similar to tag dispatching: it is entirely used to determine the return type and the actual temporary should be optimized away by the compiler.




回答2:


Your issue is not template specialization, but an overload ambiguity.

This is similar:

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

Having a template where U has T as default parameter will not do better:

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
}

And a partial specialization is not allowed:

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();
}


来源:https://stackoverflow.com/questions/21692406/partial-template-specialization-by-overloading

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!