18

Inspired from this answer, which claims to subvert the access control system, I wrote the following minimal version of the hack

template<typename T>
inline T memptr{};

template<auto Val>
struct setptr
{
    struct setter { setter() { memptr<decltype(Val)> = Val; } };
    static setter s;
};

template<auto Val>
typename setptr<Val>::setter setptr<Val>::s{};

which is then used as

class S
{
    int i;
};

template struct setptr<&S::i>;

auto no_privacy(S& s)
{
    return s.*memptr<int S::*>;
}

Why doesn't template struct setptr<&S::i>; violate access control?

Is it because [class.access]

Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions.

specifically doesn't include instantiations? In which case, why doesn't it include instantiations?

Errata: Explicit instantiations are also classified as declarations.

Passer By
  • 19,325
  • 6
  • 49
  • 96
  • See also http://www.gotw.ca/gotw/076.htm – Jesper Juhl Feb 27 '19 at 16:16
  • We actually use this "hack" in *one* place in our code base to be able to call a private member function in a library that we *really need to* call ;-) But, I suspect it could also be useful in test frameworks where you may want to test protected and private members without having to inject friends and other stuff. – Jesper Juhl Feb 27 '19 at 16:17
  • If you follow a few links from the answer, you get to [this](https://gist.github.com/dabrahams/1528856) commented version, where it is elaborated that explicit instantiations are the only place where you can form member function/field pointers while ignoring access. There is even more explanation in [this](https://github.com/hliberacki/cpp-member-accessor) library linked in the comments to an answer in the question you linked. – Max Langhof Feb 27 '19 at 16:20
  • @MaxLanghof I actually saw those, but couldn't find the source of their claims. – Passer By Feb 27 '19 at 16:27
  • We used to use this to workaround buggy `std::fstream` implementation in older visual studio version by accessing the underlying `FILE` handle – Alan Birtles Feb 27 '19 at 16:28
  • @Jarod42 [Huh?](https://godbolt.org/z/dr8bH9) Do inline variables have to be initialized? – Passer By Feb 27 '19 at 16:29
  • @PasserBy: [Clang link error](http://coliru.stacked-crooked.com/a/663fc44a027de678) and [No errors](http://coliru.stacked-crooked.com/a/a4e55a9aa5d33392). – Jarod42 Feb 27 '19 at 16:33
  • @Jarod42 Seems like a bug to me. [basic.def]/2 specifies declarations are also definitions unless its one of the items in the list, of which there isn't inline variable declarations. – Passer By Feb 27 '19 at 16:41

1 Answers1

11

From [temp.spec]/6 (emphasis mine):

The usual access checking rules do not apply to names in a declaration of an explicit instantiation or explicit specialization, with the exception of names appearing in a function body, default argument, base-clause, member-specification, enumerator-list, or static data member or variable template initializer. [ 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 that would normally not be accessible. — end note  ]

So this technique you see abuses this rule, which is primarely there to allow implementers of a class specialize templates (such as traits) with private types or other private entities

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141