Friday, November 14, 2008

c++: name lookup changes in g++ 4.3

Recently I've failed to compile my code with g++ 4.3. The error message was something like this:

test.cc:2: error: changes meaning of ‘A’ from ‘class A’
I've created a test case to discover a problem:
class A {};

class B
{
    void foo(const A &a){}
    void A(){}
};
If you try to compile this code with g++ 4.3 you will definitely get
(~~) g++ test.cc -c
test.cc:7: error: declaration of ‘void B::A()’
test.cc:2: error: changes meaning of ‘A’ from ‘class A’
What's going on here?
Method A from class B changes the meaning of the type of the parameter in method foo.
gcc 4.3 now errors out on certain situations in c++ where a given name may refer to more than one type or function. Here name A refer to function B::A and class A.

The reason that this isn't allowed in c++ is because if in the definition of B we write A(), it is ambiguous whether we want to instantiate an object of type class A or call this->A().
It's possible to fix such code in two ways.
To rename one of the names. It's not always possible if you really want these names to stay. There not a lot of synonyms :).
The second one is more technical: to move one of the names such that it is not in the scope:
class A {};

class B
{
    void foo(const ::A &a){}
    void A(){}
};
This code would be compiled w/o any errors from gcc side.

In release notes to gcc 4.3 it's additionally mentioned that -fpermissive option can be used as a temporary workaround to convert the error into a warning until the code is fixed.

I wonder how gcc made a deal with this in previous versions. The next code won't compile with g++ 4.3 but you will succeed with -fpermissive option. Let's see how g++ deal with this ambiguous situation.
#include <iostream>

using namespace std;

class A
{
    public:
        A()
        {  
            std::cout << "A::A" << std::endl;
        }  
        void operator ()()
        {  
            std::cout << "A::operator ()" << std::endl;
        }  
};

class B
{
    public:
        void operator ()()
        {  
            std::cout << "B::operator ()" << std::endl;
        }  
        void foo(A a) 
        {  
        }  
        B A()
        {  
            std::cout << "B::A" << std::endl;

            return B();
        }  
        void bar()
        {  
            ::A()();
            A()();
        }  
};

int main(int argc, char **argv)
{
    B b; 
    b.bar();

    return 0; 
}
(~~) g++ test.cc -fpermissive -o test
test.cc:34: warning: declaration of ‘B B::A()’
test.cc:7: warning: changes meaning of ‘A’ from ‘class A’
(~~) ./test 
A::A
A::operator ()
B::A
B::operator ()
You can see that function B::A will be used in the meaning of current scope, we have to use scope resolution operator to get class A constructed.
To be honest I always expected such behavior, not sure why this should be an ambiguous.
If we use name A, we use the most close to the current scope, to use name from the other namespace we use namespace resolution operator.

No comments: