Monday, July 23, 2012

Dear Lazyweb: Who is right g++ or clang?

#include <stdio.h>

class A
{
    protected:
        static void foo()
        {
            printf("HI\n");
        }
};

class B : public A
{
    friend class C;
};

class C
{
    public:
        void useFoo()
        {
            A::foo();
        }
};

int main(int argc, char **argv)
{
    C c;
    c.useFoo();
}

g++ compiles, clang complains that A::foo is protected and can't be used in C::useFoo.I know friendship is not inherited, but this is actually the reverse of inheriting, so does anyone has any pointer as to where i should open the bug? clang or gcc?

17 comments:

Anonymous said...

This is a clang bug, see http://llvm.org/bugs/show_bug.cgi?id=6840

Anonymous said...

gcc ;)

Because you use the baseclass A to call the function foo(). In this case the compiler should complain that A::foo is protected, but it should compile with B::foo.

Anonymous said...

Both or none or as you want.
It is a different appreciation of the C++ standard.
Source

Djuro Drljaca said...

"... so does anyone has any pointer as to where i should open the bug? clang or gcc?"

Why not both and let compiler developers decide since they should know the specification a little better that "normal" developers :)

Anonymous said...

I must admit I agree with the clang developer.

It seems like a bug in the c++ specification, if it states to be ok.

Never in a million years would I expect the code to compile, and the code İsmail Dönmez refer to in the bug report is different (if that doesn't build it should be fixed), but the example in this blog seems really wrong to me and gcc should be fixed

Christoph said...

I would say that GCC is ok. Here is what c++03 says about it in 11.2.4:


A member m is accessible when named in class N if
- ...
— m as a member of N is protected, and the reference occurs in a member or friend of class N, or in a member or friend of a class P derived from N, where m as a member of P is private or protected, or
- ...

We set m := foo, N := A and P := B and the clause fits perfectly.

Anonymous said...

Clang is far away from reaching GCC maturity and completeness, so you can be nearly sure GCC is right. I wouldn't even bother to messing with clang, because it's more like a toy right now.

Anonymous said...

I also think gcc is right, and it's also making sense (opposed to what Anonymouse wrote above): "friend class C" says that the class C has access to everything class B has access to (plus everything C has access to anyway). A function in B can do "A::foo()", so a function in C should be able to do so as well.

Anonymous said...

> "friend class C" says that the class C has access to everything class B has access to

Then let's modify the example:
#include

class A
{
protected:void bar() const
{
printf("Mo\n");
}
};

class B : public A
{
friend class C;
};

class C
{
public:
void useBar(A const& a) const
{
a.bar();
}
};

int main(int argc, char **argv)
{
C c;
B b;
c.useBar(b);
}

-> oops ;)
According to my understanding of name lookup, foo() is qualified with "A", so the lookup is done in "A". A foo() can be found, but it is not accessible, so the code should not compile.

SexyMimi said...

To everyone, maybe that's what the standard says, but no, it doesn't make sense.
Class C isn't friend with A but B. I don't see why the compiler should look into the class B.

Olivier Goffart said...

Yeah,
I encountered this problem while building QtWebkit, and even had made a patch for clang some years ago, but it was rejected because the developer beleived it is a "bug" in the specification. http://lists.cs.uiuc.edu/pipermail/cfe-dev/2010-June/009310.html

@SexyMimi

It makes sens if you consider that C is a "helper" class of B, so C should access everything that B can access.

In that very case, it can be worked around by calling C::foo().

Christoph said...

@Olivier Goffart it seems as if some developers confuse access control with security features. I see no other explanation for such a rejection.

Eduard said...

This is an old & well known way to work around the private / protected access rights in C++: deriving the class and declare the derived class as 'friend' of the class / function from which you want to access the function.

I don't know if this is intentional or a breach in C++ model, all I can say is that it has always worked like that, both in G++ and MSVC (just to add another, unrelated compiler to the two discussed).

I can't say it does make sense, though. My feelings is that is kinda "bug" in C++ specification, but if clang behaves differently it will cause problems compiling existing code...

STiAT said...

Well, if you look at the mailing list, you can clearly see of the conversation that at least one clang developer sees this as a mistake in the C++ standard, and that they currently have no plan on fixing it. To me, some things will need patches, since it's a quite common structure to work around protected (http://lists.cs.uiuc.edu/pipermail/cfe-dev/2010-June/009320.html)

>> By that you mean you would accept the patch?

>No, sorry, not without some more support. It's a hard question — at what point does an apparent drafting mistake in the standard become obligatory? — but I am comfortable with my current answer here.

Anonymous said...

How a million years fly (I'm commenter #4)

After reading Olivier's comment and assumming B::foo could be a different implementation than A::foo, and so the only way to access the A impl would be with A::foo

Since C is a friend it should indeed be able to access what B accesses, and so while it seemed illogical at first sight I must now admit I think the spec and g++ is right.

Justin said...

I would say g++ is correct in this instance. C and B are friend's but that's where the relationship ends, C should not be able to call A's private. C could use it's friendship with B to call A, but your code tries to call A directly. I would say that this is a call by proxy example.

IMHO.

Chris said...

g++ is right. I tried also some Borland's and Microsoft compilers, and they don't complain.