Are private class members really private? It turns out to be not quite so. There exists a mechanism that allows us to access any private member data or function, with the only requirement of knowing its type.

There is an impressive article on this topic, but I’ve found it to be a bit dense and hard to read, so I decided to add some clarifications.

Idea

The key ingredient is [temp.explicit]/12, Working Draft from 2017-03-21:

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 we explicitly instantiate a struct template<class Ptr, Ptr TargetPointer, size_t InstantiationId> struct steal which contains a static field essence whose constructor performs an assignment of the target pointer-to-member TargetPointer to the global variable template<class Ptr, size_t InstantiationId> Ptr result.

The role of the InstantiationId param is to distinguish between different values of the template variable result in case of instantiating it with the same class Ptr parameter.

Since the object essence has static storage duration ([basic.stc.static]/4), it gets initialized at the program initiation ([basic.start.static]/1); therefore, result<Ptr, InstantiationId> gets assigned at the program initiation, before the execution of main begins.

The actual access to the target member is then done via usual pointer-to-member dereferencing.

Code

Here is the idea described above, implemented in code.

namespace essence {
template<class Ptr, size_t Id>
Ptr result;

template<class Ptr, Ptr TargetPointer, size_t InstantiationId>
struct steal {
    struct exec_on_instantiation {
        exec_on_instantiation() { 
            result<Ptr, InstantiationId> = TargetPointer;
        }
    };
    static exec_on_instantiation essence;
};
template <typename Ptr, Ptr TargetPointer, size_t InstantiationId>
typename steal<
    Ptr, 
    TargetPointer, 
    InstantiationId
>::exec_on_instantiation steal<
    Ptr, 
    TargetPointer, 
    InstantiationId
>::essence;
}  // namespace essence

Demo

Here is a demo. See it in action here: https://ideone.com/Gnjvn2

#include <iostream>

using std::string;
using std::cout;
using std::endl;

class Victim {
    void f() { cout << "PoC - member function f" << endl; }
    void h() { cout << "PoC - member function h" << endl; }
    string str = "PoC - member data";
    static void g() { cout << "PoC - static member function" << endl; }
    static string static_str;
};
string Victim::static_str = "PoC - static member data";

template struct essence::steal<decltype(&Victim::f), &Victim::f, 1>;
template struct essence::steal<decltype(&Victim::h), &Victim::h, 2>;
template struct essence::steal<decltype(&Victim::str), &Victim::str, 3>;
template struct essence::steal<decltype(&Victim::g), &Victim::g, 4>;
template struct essence::steal<
    decltype(&Victim::static_str), 
    &Victim::static_str, 
    5
>;

int main() {
    auto ptr1 = essence::result<void (Victim::*)(), 1>;
    auto ptr2 = essence::result<void (Victim::*)(), 2>;
    auto ptr3 = essence::result<string (Victim::*), 3>;
    auto ptr4 = essence::result<void(*)(), 4>;
    auto ptr5 = essence::result<string*, 5>;

    Victim a;

    (a.*ptr1)();
    (a.*ptr2)();
    a.*ptr3 += " (modified)", cout << a.*ptr3 << endl;
    ptr4();
    *ptr5 += " (modified)", cout << *ptr5 << endl;
}