Search code examples
c++c++11templatesfunction-templates-overloading

Function Template Overloading with Different Return Types


Following code snippets are from Function template overloading.

How is it possible to overload functions/function templates with return type A<I+J> vs A<I-J>?

Did the page really mean that overload #1 & overload #2 compose valid function overload set?

Or, have I misunderstood the meaning of the page?

template<int I, int J>
A<I+J> f(A<I>, A<J>); // overload #1
 
template<int K, int L>
A<K+L> f(A<K>, A<L>); // same as #1
 
template<int I, int J>
A<I-J> f(A<I>, A<J>); // overload #2

I have tried the following in https://godbolt.org, which did not compile as expected with errors:

ASM generation compiler returned: 1
<source>: In function 'int main()':
<source>:48:24: error: call of overloaded 'func<1, 2>(A<1>, A<2>)' is ambiguous
   48 |     A<-1> a = func<1,2>(A<1>{}, A<2>{});
      |               ~~~~~~~~~^~~~~~~~~~~~~~~~
<source>:35:8: note: candidate: 'A<(I + J)> func(A<I>, A<J>) [with int I = 1; int J = 2]'
   35 | A<I+J> func(A<I>, A<J>) {
      |        ^~~~
<source>:41:8: note: candidate: 'A<(I - J)> func(A<I>, A<J>) [with int I = 1; int J = 2]'
   41 | A<I-J> func(A<I>, A<J>) {
      |        ^~~~
template <int>
struct A {
};

template <int I, int J>
A<I+J> func(A<I>, A<J>) {
    std::cout << "func1\n";
    return A<I+J>{};
}

template <int I, int J>
A<I-J> func(A<I>, A<J>) {
    std::cout << "func2\n";
    return A<I-J>{};
}

int main()
{
    A<-1> a = func<1,2>(A<1>{}, A<2>{});
    return 0;
}

Solution

  • Templates which differ by return types only can still have a well-formed overload resolution if you cast the function explicitly when using in the client code. This is the only known to me way to disambiguate such templates, however, so the application is quite narrow and inconvenient:

    template<int I>
    struct A {};
    
    template<int I, int J>
    A<I+J> f(A<I>, A<J>) {
        std::cout << "Overload 1" << std::endl;
        return A<I+J>{};
    }
    
    template<int I, int J>
    A<I-J> f(A<I>, A<J>) {
        std::cout << "Overload 2" << std::endl;
        return A<I-J>{};
    }
    
    int main() {
        // well-formed
        auto a = static_cast<A<-1>(&)(A<1>, A<2>)>(f)(A<1>{}, A<2>{});
    }