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:

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

    ReplyDelete
  2. 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.

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

    ReplyDelete
  4. "... 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 :)

    ReplyDelete
  5. 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

    ReplyDelete
  6. 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.

    ReplyDelete
  7. 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.

    ReplyDelete
  8. 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.

    ReplyDelete
  9. > "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.

    ReplyDelete
  10. 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.

    ReplyDelete
  11. 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().

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

    ReplyDelete
  13. 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...

    ReplyDelete
  14. 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.

    ReplyDelete
  15. 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.

    ReplyDelete
  16. 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.

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

    ReplyDelete