2

I've come across this approach (as one of the solutions to a programming task in a C++ course) a few days ago.

#include <iostream>

struct C {
    C(int i) { m_i = i; }
private:
    int m_i;
};

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

template<typename D>
struct AcessorTag {
    typedef D C::*type;
};

template struct Rob<AcessorTag<int>, &C::m_i>;

int &get_c(C &cls) {
    return cls.*get(AcessorTag<int>());
}

int main()
{
    C c(13);
    std::cout << get_c(c);
    return 0;
}

Can you please explain why this code compiles? Pretty much all that happens here is 1) we pass pointer-to-member as an argument to a struct template 2) we declare a friend function that just returns that pointer to member.

Is this standard C++? (I have tested on VS 2015)

The get function is a friend of struct Rob<> but it isn't a frient of struct C. Anyway, struct C appears to have no friends so how come its private member is accessible?

Thanks!

Alexander Chertov
  • 2,070
  • 13
  • 16
  • 1
    Doesn't compile with gcc, though the error there is `t.cpp:25:38: error: ‘get’ was not declared in this scope`. I'm suprised VS doesn't flag that error. – Chris Dodd Apr 10 '18 at 16:20
  • 2
    as a side note a good old fashion protect against Murphy and not Macchiavelli lesson http://www.gotw.ca/gotw/076.htm – Alessandro Teruzzi Apr 10 '18 at 16:27
  • [It can be fixed by writing forward declaration of function `int C::* get(AcessorTag);`](https://wandbox.org/permlink/oopiqKiFNWpaCp5N). – user7860670 Apr 10 '18 at 16:48

2 Answers2

1

Aside from missing forward declaration of get function this code should work fine and is standard compliant. This code works because of the special rule applied for explicit instantiation definitions of templates:

17.8.2 Explicit instantiation [temp.explicit]

14 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. —end note ]

So writing

template struct Rob<AcessorTag<int>, &C::m_i>;

will bypass usual access checks and make a pointer to otherwise inaccessible member available to get function body.

Motivation for such a fancy rule has been nicely described in this answer.

user7860670
  • 35,849
  • 4
  • 58
  • 84
-1

Of course, your code does not compile with gnu c++ and clang++; the identifier get is not a member of C. Even if you try to instantiate Rob, e.g.

Rob<AcessorTag<int>, &C::m_i> rob;

you obtain the

error: 'int C::m_i' is private.

You can always ask Microsoft if their c++ compiler is conforming to any standards. Let me know their answer if they answer at all.

apostol
  • 59
  • 1
  • 4
  • Writing a forward declaration of `get` function is all it takes to make both gcc and clang happy with this code. VC++ compiler behavior regarding this matter has been already fixed in VS2017. – user7860670 Apr 10 '18 at 17:23