Meditation, The Art of Exploitation

Thinking? At last I have discovered it--thought; this alone is inseparable from me. I am, I exist--that is certain. But for how long? For as long as I am thinking. For it could be, that were I totally to cease from thinking, I should totally cease to exist....I am, then, in the strict sense only a thing that thinks.

Tuesday, November 20, 2007

C++: function visibility and accessibility: lookup and overloading

C++ has some esoteric feature concerning private class member names visibility and accessibility. By definition, a private class member name is only accessible by the class members and friends. However the rule of visibility can confuse many programmers. Herb Sutter has summarized these rules in a great article '(Mostly) Private' published on DDJ.

1. A private member's name is only accessible to other members and friends.
2. A private member is visible to all code that sees the class's definition. This means that its parameter types must be declared even if they can never be needed in this translation unit...
3. Overload resolution happens before accessibility checking.

Take the following example:



// http://www.ddj.com/cpp/184403867
// (Mostly) Private
//
// Example 3: A partly fixed version of Example 1
//
#include < complex>

class Calc {
public:
double Twice( double d );
private:
int Twice( int i );
std::complex Twice( std::complex c );
};

int main() {
Calc c;
return c.Twice( 21 ); // error, Twice is inaccessible
}



When the compiler has to resolve the call to a function S, it does three main things, in order:
a. Before doing anything else, the compiler searches for a scope that has at least one entity named Twice and makes a list of candidates. In this case, name lookup first looks in the scope of Calc to see if there is at least one function named Twice; if there isn't, base classes and enclosing namespaces will be considered in turn, one at a time, until a scope having at least one candidate is found. In this case, though, the very first scope the compiler looks in already has an entity named Twice — in fact, it has three of them, and so that trio becomes the set of candidates. (For more information about name lookup in C++, with discussion about how it affects the way you should package your classes and their interfaces, see also Items 31-34 in Exceptional C++. [4])
b. Next, the compiler performs overload resolution to pick the unique best match out of the list of candidates. In this case, the argument is 21, which is an int, and the available overloads take a double, an int, and a complex. Clearly the int parameter is the best match for the int argument (it's an exact match and no conversions are required), and so Twice(int) is selected.
c. Finally, the compiler performs accessibility checking to determine whether the selected function can be called. In this case... boom thud splatter.

4. A private member is visible to all code that sees the class's definition. This means that ... it participates in name lookup and overload resolution and so can make calls invalid or ambiguous even though it itself could never be called.

5. Code that has access to a member can grant that access to any other code, by leaking a (name-free) pointer to that member.

References:

1. (Mostly) Private, Herb Sutter, C/C++ Users Journal, Jul 01 2003