Overload resolution - class member vs non-member

From a conversation in a different thread with adam2016, I realized I don't understand why standard is choosing one seemingly ambiguous overload over another. I contrived a simpler example to show what I dont understand.

Can someone explain why it is choosing the template non-member function over the member function, and if possible, link an article or standard clause that explains it.

The following creates an infinte loop, recursively calling void bar(Widgdet& a, Widget& b);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Example program
#include <iostream>

namespace gadget {

    template <typename T>
    void bar(T&, T&)
    {
        std::cout << "In template foo(T&, T&) where T = Widget" << std::endl;
    }

}

struct Widget {
    
    void foo(Widget b)
    {
        std::cout << "In foo(Widget b)" << std::endl;
        bar(*this, b);
    }
    
    void bar(Widget& a, Widget& b)
    {
        std::cout << "in bar(Widget& a, Widget& b)" << std::endl;
        //using gadget::bar;
        bar(a, b);
    }
};

int main()
{
    Widget w;
    Widget v;
    w.foo(v);
}


The following will not create an infinite loop, it calls gadget::bar(T&, T&) where T = Widget.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Example program
#include <iostream>

namespace gadget {

    template <typename T>
    void bar(T&, T&)
    {
        std::cout << "In template foo(T&, T&) where T = Widget" << std::endl;
    }

}

struct Widget {
    
    void foo(Widget b)
    {
        std::cout << "In foo(Widget b)" << std::endl;
        bar(*this, b);
    }
    
    void bar(Widget& a, Widget& b)
    {
        std::cout << "in bar(Widget& a, Widget& b)" << std::endl;
        using gadget::bar; 
        bar(a, b);
    }
};

int main()
{
    Widget w;
    Widget v;
    w.foo(v);
}


Why does it prefer the non-member function over the member function?
(Yes, I realize to stop the ambiguity, you can do Widget::bar(a, b), but my question remains).


_______________________________________

I assume that there's some sort of rule where it says "If there is an ambiguity with X, but you have using my_namespace::X;", it will always prefer the namespaced overload? But I'm not sure.
Last edited on
using gadget::bar; declares the name bar; at block scope.
The name of the member function is at class scope.
For unqualified lookup, block scope is searched before class scope

In particular:
A name declared within a member function hides a declaration of the same name whose scope extends to or past the end of the member function's class.

http://eel.is/c++draft/basic.scope.class
Great, thank you for the explanation and terminology.
Hi Ganado and JL

that makes sense,but from your(Ganados) example in my thread I will post the example below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

#include <iostream>

// template defined first
template <typename T>
void foo(T& t)
{
    std::cout << "template T& foo" << std::endl;   
}

void foo(int& t)
{
    std::cout << "int& foo" << std::endl;   
}

// int& defined first
void bar(int& t)
{
    std::cout << "int& bar" << std::endl;   
}

template <typename T>
void bar(T& t)
{
    std::cout << "template T& bar" << std::endl;   
}

int main()
{
    int t = 0;
    foo(t); // calls int& version, not template version
    bar(t);  // calls int& version, not template version
}


does this still hold? does the compiler choose a non templated function before the templated function?

thanks
Last edited on
> does the compiler choose a non templated function before the templated function?

Yes, if the implicit conversion sequence of the arguments for the non-template function is at least as good as that for the instantiated template.

For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

void foo(int&) { std::cout << "non-template void foo(int&)\n" ; }
template < typename T > void foo(T&) { std::cout << "template void foo(T&)\n" ; }

void bar( const int& ) { std::cout << "non-template void bar( const int& )\n" ; }
template < typename T > void bar(T&) { std::cout << "template void bar(T&)\n" ; }

int main()
{
    int i = 7 ;

    foo(i) ; // non-template void foo(int&)

    // the non-template function would require a qualification conversion (add const)
    bar(i) ; // template void bar(T&)
}

http://coliru.stacked-crooked.com/a/d6e9db86527c10b0
thanks Jl :)
Topic archived. No new replies allowed.