11

Item 23 of Effective C++ (3rd edition) by Scott Meyers is titled: "Prefer non-member non-friend functions to member functions". The reason Scott suggests is the increase of encapsulation. So, only the functions that need to access the private members are made member functions, the other functions are made free-standing.

Now, the user does not care about my considerations. All he wants is a clean uniform interface that is as simple to learn as possible. Having some functions as members and some as free-standing seems to break this uniformity. Furthermore, the choice of which function is member and which is free-standing may seem random to the user, who is unaware of the implementation details (i.e. the private members) of my class.

One answer even cites a language proposal related to this issue.

My question is: is it a good idea to always provide a free-standing wrapper function for each member function in order to present the user with a uniform interface consisting of free-standing functions only?

8
  • No. Scott's technique clearly indicates the functions that access state from the functions that don't. Your wrapper functions would clobber that separation. The non-member functions enable encapsulation of state. The free-standing functions enable immutable, functional-style programming, a technique that is clearly communicated by their use. That the functions be all free standing or all not free standing in the quest for uniformity is completely unimportant. Feb 17, 2016 at 14:50
  • @RobertHarvey Why does the user care about that separation? The author of the class cares, but he can always go to the class declaration to see the list of member functions... Feb 17, 2016 at 14:52
  • Presumably the user is a programmer, if we're talking about classes and free-standing functions. If they user is programming in C++, he is almost certainly informed enough to use the functions properly, in their intended paradigm, and communicating their proper use by making them member or free-standing is just polite. Feb 17, 2016 at 14:53
  • 1
    @RobertHarvey Sure, but he has other things to keep in his head besides remembering which functions are called in which way. The time to look that up is expensive as well. Moreover, he does not get any additional value from the separation, does he? Feb 17, 2016 at 14:55
  • Most programmers nowadays use a modern IDE that already tells them this information anyway. The value from the separation is not so much knowing when you're calling a member function and possibly modifying state (which should already be obvious), but knowing that, in a free standing function, you can almost certainly not have to worry about side-effects occurring (i.e. modifying state). This enables an immutable, functional programming style that is quite powerful for things like concurrency. You won't have that capability if you have no idea if side-effects are occurring on function calls. Feb 17, 2016 at 14:59

1 Answer 1

5

To directly answer the question you asked, I'd say the short answer is no: it's not necessarily worthwhile writing a wrapper for every member function, just to give a uniform interface.

That said, it can be worthwhile to do so if you have something that corresponds closely to a concept, that's defined entirely in terms of free functions, and by providing free-function wrappers, your class can model that concept. In this case, having those wrappers lets you use objects of that class with some set of templates that wouldn't otherwise work with them. The question at that point is whether those templates are likely to be useful with objects of this particular class. Even if the class happens to support the syntax necessary to model the concept, the operation(s) provided by those templates may not be useful with it--in which case, it's obviously pretty pointless.

At least in my view, providing wrappers so x.f(y) can be called like f(x,y), just so the programmer doesn't need to pay attention to whether f is a free function or a member function is generally misplaced. Yes, in a completely ideal world where time wasn't constrained at all, it might be worth considering. Likewise, in a few cases a class might be used so widely and heavily that it's worth considering putting in the extra time to write the wrapper in the hope of its paying off in the long run by making everybody else's lives easier.

Unfortunately, I don't think either of these applies very often. Most of us have way too large of a backlog of problems that really need fixing to give it serious consideration, at least until we have solid, objective evidence that the investment will pay off, and fairly soon at that. Doing it just to fulfill a principle without a fairly solid assurance of real benefits would be difficult to justify (at best).

6
  • This is a very pragmatic approach. Just to clarify the borders of this approach. Suppose I wrote two dozen of classes while being ignorant of Item 23 of Scott Meyers, so all functions are members. Would you suggest that I re-work these classes to conform to the principles of encapsulation? Feb 17, 2016 at 17:16
  • @AlwaysLearning: I'd put it into the backlog of items to do. The only real question is what priority it's given. I don't have a simple answer to that though--it basically comes down to a question of how much difficulty it's causing you relative to whatever other issues you have in your backlog. Feb 17, 2016 at 18:43
  • 1
    There is a paper about introducing unified call syntax for C++ (more). Feb 17, 2016 at 19:43
  • 1
    @Deduplicator: There is a paper, and in a few years it might render this entire question moot (at least in new code for new compilers). But it's not really ans answer to anything right now, and while I think some variant of the proposal will probably be accepted eventually, we have to keep in mind that it may not happen at all--and even if it does, it may be a long time, in severely reduced form (cf. Concepts). Feb 17, 2016 at 20:11
  • Just thought it might worth mentioning it here, despite those caveats. Which you are most certainly right about. Feb 17, 2016 at 20:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.