18

From a blog post Access to private members: Safer nastiness by Johannes Schaub - litb:

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};

// use
struct A {
  A(int a):a(a) { }
private:
  int a;
};

// tag used to access A::a
struct A_f { 
  typedef int A::*type;
  friend type get(A_f);
};

template struct Rob<A_f, &A::a>;

int main() {
  A a(42);
  std::cout << "proof: " << a.*get(A_f()) << std::endl;
}

how get function can be call from a object since its not defined inside class A ?

EDIT:

I don't understand why get must have Tag as parameter instead of a.*get<A_f>() => ok it's due to ADL mechanism

Community
  • 1
  • 1
Guillaume Paris
  • 10,303
  • 14
  • 70
  • 145
  • Seems to be a weird bug when it comes to Argument Depending Lookup (ADL), I'm currently on my blackberry and can't play around with it - but if that compiles it should (from my point of view) be treated as a bug. – Filip Roséen - refp Oct 20 '12 at 22:39
  • Has anyone confirmed this to work on any compilers other than gcc? – hexist Oct 20 '12 at 22:59
  • 1
    @hexist I just verified this on Clang (3.1) and Intel C++ (13.0.0). – Nikos C. Oct 20 '12 at 23:08
  • 1
    I think the core question is why the compiler doesn't abort on `template struct Rob;` with something like "A::a is private within this context". After all, the code is trying to get a pointer to a private member. Weird. – Nikos C. Oct 20 '12 at 23:32
  • 1
    @hexist add Apple LLVM 4.1 to the list that this works on. Seeing a pattern here... – WhozCraig Oct 21 '12 at 00:30
  • Very interesting, I would very much like to know why exactly all the compilers think tihs is legal. @NikosChantziaras boiled down the (real imho) question very well.. how is it legal that one can use this to resolve `&A::a` from outside of `A`. I'm wondering if it's an unintended consequence of something defined in the c++ std, or if it's really intended to work like this. – hexist Oct 21 '12 at 00:40
  • 2
    @JamesMcNellis, I think your link is wrong there. Try: http://bloglitb.blogspot.com/2011/12/access-to-private-members-safer.html – bdonlan Oct 21 '12 at 10:41
  • @bdonlan: Oops, thanks. Yes, that's where the code is from. – James McNellis Oct 21 '12 at 17:26

3 Answers3

8

You are not calling get from a! Actually what get return is a class pointer to a member inside A and type of it is int A::* so you need an instance of A to access that value.

For example let me play a little with your code:

struct A {
    A(int a):a(a) { }
    int b;
private:
    int a;
};
void test() {
    auto p = &A::b;
    std::cout << a.*p << std::endl;
}

Did I call p from inside a? a does not have p, this is exactly what happened in your code, get function return &A::a and you use a to read its value! that's all, nothing is wrong and I think it will be compiled in all compilers.

One other question here is: Why C++ allow declaring template using private member of A. C++ standard say:

14.7.2p8 The usual access checking rules do not apply to names used to specify explicit instantiations. [Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible.]

But if you try to instantiate or even typedef specified template then you get an error. Let's modify your example slightly:

struct A {
private:
    int a;
    friend void f();
};

// Explicit instantiation - OK, no access checks
template struct Rob<A_f, &A::a>;

// Try to use the type in some way - get an error.
struct Rob<A_f, &A::a> r;            // error
typedef struct Rob<A_f, &A::a> R;    // error
void g(struct Rob<A_f, &A::a>);      // error

// However, it's Ok inside a friend function.
void f() {
    Rob<A_f, &A::a> r;               // OK
    typedef Rob<A_f, &A::a> R;       // OK
}
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
BigBoss
  • 6,904
  • 2
  • 23
  • 38
1

It's legal because friend functions are always in the global scope, even if you implement them inside a class. In other words, this:

class A
{
    friend void go() {}
};

is just a shortcut for:

class A
{
    friend void go();
};

void go() {}
user1610015
  • 6,561
  • 2
  • 15
  • 18
1

This is a known compiler bug in gcc and was fixed in a later release. See-:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41437

bootkick
  • 482
  • 1
  • 5
  • 18