6

Here are two template functions, that differ only in their template parameters. The rest of the parameters are exactly the same.

    template<int module>
    void template_const(int &a,int & b){
            a = a & module;
            b = b % module;
    }

    template<bool x>
    void template_const(int &a,int & b){
            int w;
            if (x){
                    w = 123;
            }
            else w = 512;
            a = a & w;
            b = b % w;
    }

When I try to call them like this

template_const<true>(a,b)

or

template_const<123>(a,b)

the compiler tells me that the call is ambiguous. How can I call these two functions?

Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
worldterminator
  • 2,968
  • 6
  • 33
  • 52
  • g++ 12.1 does not bailing out with `template_const<123>(a,b)`. I don't know for other compilers. Of course it bails out with `template_const<1>(a,b)` or `template_const<0>(a,b)`. If you not have special problem, use `template requires(module != 0 && module != 1)` which is C++20. Another solution is to replace bool with `enum`. – Chameleon Aug 23 '22 at 17:53

2 Answers2

7

As @jogojapan pointed out, the problem is that the compiler cannot order these two functions, i.e. there is not one that is more specialized than the other. As explained in §14.5.6.2, when a call to an overloaded function template is ambiguous, the compiler uses a partial ordering between the various overloads to select the most specialized one.

To order the overloads, the compiler transforms each one of them and performs template argument deduction to see if one is more specialized than another one (there is a short explanation at the end of this answer). In your case, the two overloads are equivalent (or not comparable): template<int> void template_const(int &,int &) is not more specialized than template<bool> void template_const(int &, int &), and vice-versa.

Therefore, the compiler cannot select one over the other, hence generating an ambiguous call error.


If you are ok with explicitely specifying the type of the parameter you want to pass, you can use partial template specialization as follow:

template<typename T, T param>
struct template_const_impl;

template <int module>
struct template_const_impl<int, module>
{
    static void apply(int &a, int &b)
    {
        a = a & module;
        b = b % module;
    }
};

template<bool x>
struct template_const_impl<bool, x>
{
    static void apply(int &a, int &b)
    {
        const int w = x ? 123 : 512;
        a = a & w;
        b = b % w;
    }
};

template <typename T, T param>
void template_const(int &a, int &b)
{
    return template_const_impl<T, param>::apply(a, b);
}

int main()
{
    int i = 512, j = 256;
    template_const<int, 123>(i, j);
    template_const<bool, true>(i, j);
}

This is not ideal, but it don't think there is a cleaner solution unless you can use C++11 and are willing to rely on some macros, in which case you can simplify the calling code a bit (idea taken from @Nawaz in this answer):

#define TEMPLATE_CONST(x) template_const<decltype(x), x>

int main()
{
    int i = 512, j = 256;
    TEMPLATE_CONST(123)(i, j);
    TEMPLATE_CONST(true)(i, j);
}
Community
  • 1
  • 1
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
0

I do not think it is going to work like this. You have overloads with same parameter types. Probably you will have to give them different names in the end and call them as you tried.

Slava
  • 1,528
  • 1
  • 15
  • 23
  • But they passed the compilation. Does template is compiled when they are actually called? – worldterminator Jun 26 '13 at 07:25
  • compiler tells you call is ambigous, right? that is not called passed the compilation. Everything is compiled at compile-time of course, templates are only compiled when an instantiation is requested. – Slava Jun 26 '13 at 07:41
  • 1
    It's true that it is not going to work like this. But the problem is not simply the fact that the function parameter types are the same. You can overload a function template merely by altering the template parameter (not the function argument types). The problem here is that the disambiguation mechanism used during template overload resolution considers `bool` and `int` as equal (unlike the resolution mechanism used for ordinary function arguments). – jogojapan Jun 26 '13 at 08:05