5

One thing I'd like to do in C++ is to call non-member functions with the same syntax you call member functions:

class A { };
void f( A & this ) { /* ... */ }
// ...
A a;
a.f(); // this is the same as f(a);

Of course this could only work as long as

  • f is not virtual (since it cannot appear in A's virtual table.

  • f doesn't need to access A's non-public members.

  • f doesn't conflict with a function declared in A (A::f).

I'd like such a syntax because in my opinion it would be quite comfortable and would push good habits:

  1. calling str.strip() on a std::string (where strip is a function defined by the user) would sound a lot better than calling strip( str );.

  2. most of the times (always?) classes provide some member functions which don't require to be member (ie: are not virtual and don't use non-public members). This breaks encapsulation, but is the most practical thing to do (due to point 1).

My question here is: what do you think of such feature? Do you think it would be something nice, or something that would introduce more issues than the ones it aims to solve? Could it make sense to propose such a feature to the next standard (the one after C++0x)?


Of course this is just a brief description of this idea; it is not complete; we'd probably need to explicitly mark a function with a special keyword to let it work like this and many other stuff.

2
  • Why? You propose a syntactical change, which is severly limited and doesn't help much (at most saves one character). Don't you think C++ is complex enough already?
    – user7043
    Jan 17, 2011 at 17:11
  • Four people with the same generic icon. Ahhh them confusion yo have just caused my poor little brain. Jan 18, 2011 at 1:17

6 Answers 6

18

re your #2: Actually, making functions non-members often increases encapsulation, as Scott Meyers observed more than a decade ago.

Anyway, what you describe sounds much like C#'s extension methods to me. They are good to soothe the minds of those that are frightened when they see totally free functions. :) Once you've braved the STL (which, BTW, is not the standard library, but only that part of it which comes from the original STL) where almost all functions are so free that they aren't even real functions, but function templates, you will no longer need those.

In short: Don't try to bend a language to adapt to your mindset. Instead enhance your mindset to embrace the language's philosophy. You will emerge a bit taller from doing so.

10
  • 1
    Yeah, that's exactly my point. Using non-member functions increases encapsulation, but most of the times, classes provide some member functions that don't need to be members, and this common practice breaks encapsulation.
    – peoro
    Jan 17, 2011 at 14:37
  • @peoro: If using non-members increases encapsulation why do you want to call them as if they were members?
    – sbi
    Jan 17, 2011 at 14:39
  • Because the syntax to call a member function is more comfortable. Don't you agree that very often - almost always - classes provide some member functions which wouldn't require to be member? Why does this happen? In my opinion this happen because it is more comfortable to call a function that works on an object with the syntax object.function.
    – peoro
    Jan 17, 2011 at 14:41
  • 2
    @peoro: No, as I wrote, it's only more comfortable to those coming from so-called "pure" OO languages like Java and C#, which boggle at free function. Most will overcome this, if they persevere. When I learned C++ almost 20 years ago, developers came to C++ from C and boggled at member functions. It's a good thing they didn't adapt the syntax back then to make them more comfortable, don't you think? (Although it would be interesting to think whether languages like Java etc. would ever have been born - and what they'd look like - had C++ managed to get OO without the member function syntax.)
    – sbi
    Jan 17, 2011 at 14:45
  • 4
    @peoro, @sbi: The funny thing is that those pure-OO languages end up with weird interfaces that come from the need to encapsulate everything in a class. Take a look at java.util.Collections that is a wrapper around static functions because the language (or its followers) won't even think on having free functions... Well, those are free functions in a cumbersome namespace called class Collections.
    – David Rodríguez - dribeas
    Jan 17, 2011 at 14:52
3

Trying to answer your points:

  1. This is just a matter of style. To you it sounds better, to someone else it sounds worse. Some languages (of Lisp heritage) define all calls as (<func name> <arguments>), is it inherently better or worse than <func name>(<arguments>)? Maybe and maybe not. Matter of style. I'm sure no one is going to consider changing the C++ standard on such considerations.
  2. This is what static members are for in C++ - are you familiar with this construct? Besides, as the C++ standard library (the STL portions thereof) demonstrates, it's possible to combine objects with non-member functions that act on them in an elegant way.
7
  • I'm not getting your second point. static members don't work on an instance of the class, what has it got to do with this feature?
    – peoro
    Jan 17, 2011 at 14:35
  • Static members are similar to non-member functions in that they don't get an instance (this) passed. You can invoke them on instance of the class if you want - they act like "class-specific global functions". Factories are a good example.
    – Eli Bendersky
    Jan 17, 2011 at 14:45
  • Well, a static function taking an instance of the class as their parameter is kind of the opposite of what I'm thinking about: a static function is a way to declare a somehow non-member function that can access non-public members (similar to a free friend function?). This idea is to do the opposite: allowing to declare member functions that cannot use member variables.
    – peoro
    Jan 17, 2011 at 15:00
  • @peoro: you said "to call non-member functions with the same syntax you call member functions". Now you say something else. I'm confused
    – Eli Bendersky
    Jan 17, 2011 at 15:02
  • Yes, sorry, I don't know how to call the functions I was proposing. My point is: when a function needs to do something with an object, it is usually implemented as a member function by the developer of the class. This thing breaks encapsulation (see sbi's answer), but is done anyway because it's more comfortable to call it with the syntax obj.f(); this is why I think it's done. This makes me think: a way to define a non-member function (improving encapsulation!) which can be used as obj.f() could be a solution. A static function breaks encapsulation as much as any other member function.
    – peoro
    Jan 17, 2011 at 15:09
2

I believe that as part of Concepts (which were dropped from C++0x), there was something like this, but going in the opposite direction, so that nonmember syntax could be used to call member functions. In other words, given a std::vector<int> vec, this

begin(vec)

would be equivalent to calling the member function explicitly:

vec.begin();

I think that is a better approach, in that it is biased so that it prefers nonmember functions, which are the most "public" part of the interface. To some extent, member functions are an implementation detail which is likely to change more often than the typically higher-level nonmember functions that use the class.

So something like this would allow us to use the general non-member syntax for everything, and while it would implicitly fall back to calling a member function if no matching non-member function is found, it would always prefer the most public, the most encapsulated function available, which is the non-member one.

Of course, both ways can silently break code, but allowing nonmember syntax for calling member functions will at least maintain encapsulation: by adding a new function, I can convert a member function call, which has access to the private members of the class, into a non-member which does not have access to private members.

But if it was done the other way around, allowing member syntax for calling non-member functions, then I could, by adding a new function to the class, silently convert an unknown number of non-member function calls into member function calls. In other words, a lot of code which previously called a function that had no access to private members, are silently changed into calls to a function that does access private members. I think that's the more dangerous way to go.

1

If you want to add methods to a class, just inherit from that call and add the members to your derived class.

If you're asking how I like your syntax, I don't. Why make it look like a method is part of a class when it isn't. Especially when it's pretty straight forward to make it part of a class.

3
  • Member functions break encapsulation. Declaring some functions as member just to be able to do x.function() is a bad thing, and this idea is to give a way to avoid it. (Besides your first phrase introduces other issues: it might be a problem if the base class doesn't have a virtual destructor etc).
    – peoro
    Jan 17, 2011 at 14:39
  • 2
    This is not the right approach. You should only inherit from those classes that were designed to be derived. This type of reasoning is what leads to people deriving from standard containers, and then people seeking ways to disable inheritance and what not.
    – David Rodríguez - dribeas
    Jan 17, 2011 at 14:53
  • 1
    And on extending classes with free functions, this is a good article to read: GOTW 84 - Monoliths "Unstrung"
    – David Rodríguez - dribeas
    Jan 17, 2011 at 14:54
1

I think it's an excellent idea because of ADL and the operator overloading mess- make operators pseudo-members, ditch ADL. Extension methods like this wouldn't change encapsulation because they're still an exterior function, they just have the syntax of an interior function.

There are some functions that do well to be free, and some functions that do well to be members- even if they're free from an encapsulation point of view.

0

To me it is a value to clearly be able to see whether I am making a call to a member or nonmember function.

But even if I preferred your style, what if after writing such a call a.f(...) into my code, a function f() with a compatible signature is introduced into class A? Should my code silently break upon the next recompile, calling into a totally different function than I originally intended? Should the design committee attempt to extend the (already very complex) name lookup rules to accommodate such special cases?... And what if I will have already left the company in the meantime, and it is up to my poor successor to try and decide what my original intent could be?

1
  • There are other problems like the one you mentioned: what if two namespaces provide a non-member function f to be called with the syntax object.f on objects of the same type? If I decide this idea could be worth something I'll try to find out solutions. The simplest idea for your case is just to give an error at compile time: it's exactly like a symbol conflict; namespaces will help us.
    – peoro
    Jan 17, 2011 at 14:45

Your Answer

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

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