Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguity between function and function in namespace with same argument

Can anybody explain why there is an ambiguity between A::f(const B& b) and f(const A::B& b). I consider the code to be quite explicit about the intention.

#include <iostream>

namespace A
{
  class B
  {
  protected:
    double value_;
  public:
    B() : value_(15.0) {}
    double getValue() const {return value_;}
  };

  void f(const B& b)
  {
    std::cout << "f(b) = " << b.getValue() << std::endl;
  }
}

void f(const A::B& b)
{
  std::cout << "Other f(b) = " << b.getValue() << std::endl;
}

int main()
{
  A::B b;
  A::f(b);
  f(b);
  return 0;
}

However, both g++ 7.5.0 and clang 6.0.0 complain about ambiguous function call (error: call of overloaded ‘f(A::B&)’ is ambiguous) regardless of compiler flags and optimizations.

like image 711
rc4559 Avatar asked May 05 '21 08:05

rc4559


2 Answers

This is an example of argument-dependent lookup.

Even though ::main is in global namespace, A::f is callable without using the fully-qualified name, because it is looked up in the namespace A of its argument, A::B. As a result, there is an ambiguity between ::f and A::f.

To resolve the ambiguity, you would need to call either A::f(b) (as you did), or ::f(b).

like image 185
Jan Schultke Avatar answered Nov 20 '22 14:11

Jan Schultke


For this function call expression

f(b);

the compiler finds the name f declared in the global namespace. On the other hand, using the argument dependent lookup the compiler also looks through the namespace A where the class B is declared and also finds a function with the name f. Neither function is the best candidate to be called. So there is an ambiguity.

Apart from using qualified names of the functions to avoid the ambiguity you could suppress the argument dependent lookup the following way

( f )( b );

The argument dependent lookup is very useful for searching friend functions that otherwise are not visible if they are only defined in a class that grants the friendship.

Here is a demonstrative program

#include <iostream>

namespace N
{
    

    class A
    {
    private:
        int x;
    
    public:
        A( int x ) : x( x ) {}
    
        friend std::ostream & operator <<( std::ostream &os, const A &a )
        {
            return os << a.x;
        }
    };
}

int main()
{

    N::A a( 10 );
    
    std::cout << a << '\n';
    
    return 0;
}

The program output is

10

If the argument dependent lookup did not exist then the friend operator << could not be called because it will not be found even in the namespace N

Consider the following program.

#include <iostream>

namespace N
{
    class A
    {
    private:
        int x;
    
    public:
        A( int x ) : x( x ) {}
    
        friend void f( int x )
        {
            A a( x );
            
            std::cout << a.x << '\n';
        }
    };
}

int main()
{
    N::f( 10 );
    
    return 0;
}

The friend function f can not be called even if to specify the qualified name N::f. For this program the compiler will issue an error saying that ‘f’ is not a member of ‘N’. To make the function visible you need to declare it also outside the class A as for example

#include <iostream>

namespace N
{
    class A
    {
    private:
        int x;
    
    public:
        A( int x ) : x( x ) {}
    
        friend void f( int x )
        {
            A a( x );
            
            std::cout << a.x << '\n';
        }
    };
    
    void f( int ); // make the function visible
}

int main()
{
    N::f( 10 );
    
    return 0;
}
like image 21
Vlad from Moscow Avatar answered Nov 20 '22 16:11

Vlad from Moscow