-2

I am trying to get a feel for modern C++ idioms and best practices, and I wanted to ask if, when authoring a class, there was ever a time one should make a function a member function, instead of a free-function in the class's namespace, besides when you need runtime dispatch based on the type of the class.

I was thinking maybe it would be when you need access to private members of the class, but you could also make a free friend function and get the same effect but with the benefits of free functions, so I am not sure what is best in that case.

Is it ever justified to make a non-virtual function a member?

  • It almost seems like you're talking about a `static` *class* member; not a non-virtual member (both of which have reasons to utilize). – WhozCraig Jul 23 '14 at 03:44
  • What "benefits of free functions" are you referring to? – Wyzard Jul 23 '14 at 03:47
  • @WhozCraig no, afaik static class members don't have benefits of free functions. – user3867053 Jul 23 '14 at 03:49
  • @Wyzard customization via overloading, adl... – user3867053 Jul 23 '14 at 03:50
  • 1
    @user3867053 ADL resolving to the namespace of the class? You get that even when you do `foo.func()`. – Pradhan Jul 23 '14 at 03:51
  • @Pradhan yes, I meant you don't lose that going to free-functions. – user3867053 Jul 23 '14 at 03:56
  • Down voting the question because the author of this post seems to be debating with the answers when the consensus seems to be clearly opposite the questioner's point of view. Therefore the question doesn't seem helpful. – Brad Jul 23 '14 at 04:21
  • @BitBlitz Is that a valid reason to down-vote? This might not be the place for me to ask that though. Should probably chat at meta :) – Pradhan Jul 23 '14 at 04:23
  • @BitBlitz you're free to downvote, but I think it's unusual to downvote a question the asker of which is debating the validity of the answers. Consensus isn't very helpful if everyone is technically wrong, which is why I ask questions to help my understanding. I believe that answerers should be able to defend their answers. – user3867053 Jul 23 '14 at 04:24
  • My point was that the question seemed more an opportunity for debate than learning, but I am fairly new and will take the question to meta. – Brad Jul 23 '14 at 04:28
  • @user3867053 As I mentioned in an earlier comment, it would help both the one asking questions and the ones answering them if you used an SSCCE to get your point across. I think that was the problem here. A lot of the answers and comments you got are about standard well-accepted OOP principles. Of course, `C` was still Turing-complete. So, there isn't a fundamental difference between the sort of computations `C` can achieve and `C++` can. Everything you could do with `C`, you could do with `C++`. But then, the same is true for machine code as well :) To sum up, code please. – Pradhan Jul 23 '14 at 04:29
  • @Pradhan C doesn't have overloading or namespaces. Here is an article by Herb Sutter that explains some more about free functions: http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197 as for code, `std::list l; sort(l);` If `std::list` had an overload for `sort`, you would be able to change `std::list` to something else and it would still work, even a C-array if you wanted. – user3867053 Jul 23 '14 at 04:36
  • @BoltClock I think that just because all the answers are opinion-based at the moment, it does not make the question opinion-based. I am still waiting for actual reasons other than "everyone does it, and I prefer the syntax." – user3867053 Jul 23 '14 at 05:03
  • 3
    It's very clear that everyone is interpreting your question is one of opinion, something I can't really fault anyone for. Idioms, best practices and so on are broad and subjective by definition, there are many ways to skin a cat, and so on. These are what make a question opinion-based, which *lends* the question to opinionated answers. If you're looking for factual answers, I recommend asking a question about a specific practical problem with a specific desired solution instead. – BoltClock Jul 23 '14 at 05:06
  • FWIW, the only reason I didn't vote to close this immediately is because I thought an answer based on generally-accepted practice might be considered objective enough. Clearly that wasn't the case. :-) – Wyzard Jul 23 '14 at 05:09
  • `Is it ever justified to make a non-virtual function a member?` It usually makes sense when invariants need to be maintained and it is natural for the type to have that method. For example, operator `+=`, container's `push_back()` or `size()`, etc. But it is largely a matter of opinion and style, rather than a technical issue. – juanchopanza Jul 23 '14 at 05:14

2 Answers2

1

It's not only justified, it's typical to make non-virtual functions members of a class if they operate on data in that class.

Anything you can do with a non-virtual member function you can also do with a friend free function. The difference is whether you call the function as obj.foo() or foo(obj). In an object-oriented program, the former is preferred, and the latter is used only in (uncommon) cases where it helps to make the code more readable.

This isn't something that's changed in "modern" C++. It's always been this way.

Wyzard
  • 31,764
  • 3
  • 60
  • 78
  • But writing it that way inhibits generic code, for example, it is encouraged to use `std::begin` rather than `vector::begin`. – user3867053 Jul 23 '14 at 03:48
  • `std::begin` isn't really a member of any single class; it's a template that just wraps the `.begin` member function in a container class to adapt it for generic use. But the `.begin` member function is what does the real work, and there's little reason to use `std::begin` in non-generic code. – Wyzard Jul 23 '14 at 03:51
  • 1
    @user3867053 Perhaps you are thinking of a function template? You could give an example of two pieces of code and why you think the free function makes more sense to clarify your point. – Pradhan Jul 23 '14 at 03:54
  • 1
    @user3867053: and yet there's `std::list::sort` and `std::sort` - why? Because the former inherently can only work for `std::list` - it's taking advantage of implementation knowledge, so there's no genericity on offer anyway. – Tony Delroy Jul 23 '14 at 03:56
  • @Pradhan it assists other people in writing generic code. – user3867053 Jul 23 '14 at 03:58
  • @TonyD you can switch from `list` to something completely different not even in the standard library which doesn't have a `.sort()` member if you use an unqualified `sort` call without having to rewrite any of that code. – user3867053 Jul 23 '14 at 03:59
  • Compile-time generic C++ code is sort of a different language (or sub-language) than ordinary runtime C++ code, and it's more of a functional language rather than object-oriented. If you're writing functions that will only ever be used generically, it might make sense to define them as free functions only, but that's not the case for most code. – Wyzard Jul 23 '14 at 04:00
  • @user3867053: nonsense... if `std::list::sort` were moved to be a non-member `friend` function and the use of it to sort non-lists allowed/encouraged, then that would couple the `sort` implementation to the private members and algorithmic approach needed by `std::list` *at the point in time it was hijacked for other uses*, meaning evolution of `sort` due to changes in `std::list` implementation likely breaks the non-list usages. That's clearly unacceptable and a failure in the `list`'s overall encapsulation. – Tony Delroy Jul 23 '14 at 04:11
  • @TonyD Uh, I don't think you understand what I mean. I mean that if you use `sort(obj)` then you can change the type of `obj` and just provide an overload of `sort` for it, which you can do even if you didn't write the class of `obj`. You are thinking the implementation is reused, but I don't mean that. – user3867053 Jul 23 '14 at 04:22
  • 1
    Then feel free to call `std::sort` when you want to sort something. But if you write a new class that knows how to sort itself, give it a `sort()` member function and define its `std::sort` overload as a wrapper for that, so that you *also* support non-generic code that has an instance of your class and wants to ask it to sort itself in the OOP way. – Wyzard Jul 23 '14 at 04:30
  • Hmm, actually `std::sort` is a bad example for that it takes a pair of iterators that the `.sort` member doesn't; you'd probably want to define the `.sort` member as a wrapper for `std::sort(begin(), end())`. But `std::begin` is a better example, since it's just a wrapper for a `.begin` member. In both cases you have two interfaces to do the same thing: one strongly-typed and object-oriented, the other generic and functional. Try to support both. – Wyzard Jul 23 '14 at 04:38
  • @Wyzard not `std::sort`, just `sort` (function calls have to be unqualified to use ADL). You mention when you write a new class that knows how to sort itself, to give it a member `sort`. Why define `sort` as a member when it could be a friend function and then you wouldn't have to have the wrapper `sort` that just called the member `sort`? That is the essence of my entire question. – user3867053 Jul 23 '14 at 04:38
  • In a nutshell: to support the object-oriented programming style where variables are strongly-typed objects and you call methods on them. Not all (or even most) C++ code should be written in completely generic style. – Wyzard Jul 23 '14 at 04:40
  • @Wyzard `std::begin` does not always call `.begin()`. For example, on C-arrays it returns the pointer to the first element and `std::end` does what you think it would do. – user3867053 Jul 23 '14 at 04:40
  • @Wyzard why is `a.b()` more object-oriented than `b(a)`? – user3867053 Jul 23 '14 at 04:41
  • Because it calls a method on an object. Did you come here to ask a question about best practices, or to try to convince the world that everyone is doing OOP wrong? – Wyzard Jul 23 '14 at 04:41
  • @Wyzard that is not a reason, `b(a)` also calls a method on an object, in the same dispatch style unless `a` is a reference and `b` is virtual (which I already said is a good use of member functions). I came to ask a question and to get reasonable answers. – user3867053 Jul 23 '14 at 04:52
  • And I've provided a reasonable answer that's in line with generally-accepted C++ programming style. I'm not going to rewrite it to say "non-virtual member functions are pointless", so I don't see a purpose in continuing this thread. – Wyzard Jul 23 '14 at 04:56
  • If you believe that non-virtual member functions really are pointless, write an answer of your own — it's acceptable to answer your own question on SO — and provide a *concise, clear* explanation of why. You can even mark your own answer as the accepted one if you want to. – Wyzard Jul 23 '14 at 04:57
  • Ok, thanks for your time. I cannot write an answer now because you and BoltClock locked the question, but even if it was not locked, I would not answer it because I only _think_ they are not particularly useful when not virtual or protected, I do not know for sure. I think answers are when you think you know for sure. – user3867053 Jul 23 '14 at 05:00
  • Wow, that happened about a minute *after* I suggested writing your own answer. :-) Normally it takes five votes to lock a question, but BoltClock is a moderator. – Wyzard Jul 23 '14 at 05:02
  • "...if you use sort(obj) then you can change the type of obj and just provide an overload of sort for it, which you can do even if you didn't write the class of obj." - 1) needing friendship = editing rights to `obj`. 2) You're asking what's *idiomatic*, which *means* recognisably common practice - putting a reasonable but not bloated core of functionality directly in a class *is* idiomatic. You seem to be coming at this ala "why would you ever do X when you can do Y?", without properly documenting your notions supporting your preference for X. – Tony Delroy Jul 23 '14 at 05:15
  • On a related note: I came across [this presentation](http://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil) (linked from a comment in [another answer](https://stackoverflow.com/a/24924686/226975)) which describes an interesting way to let clients implement an interface using free functions, with the member functions hidden in a private template class. You get polymorphism (even extending to primitive types) just by writing overloaded free functions. – Wyzard Jul 24 '14 at 14:43
1

One of the main principles in OOP is encapsulation. You take some data that's logically together and make a class out of it. An extension of the same logical grouping would be functions manipulating this data. Such functions should be made member functions. Having them be free functions which are friending the class goes against the encapsulation principle.

Pradhan
  • 15,095
  • 3
  • 39
  • 56
  • I disagree that it violates encapsulation; making a friend function rather than member is just as encapsulated. – user3867053 Jul 23 '14 at 03:49
  • @user3867053 and how do you propose to make your friended free function *protected* for only derivations to consume, or is that something you no longer consider "modern" ? – WhozCraig Jul 23 '14 at 03:57
  • @WhozCraig I came here to learn, not to teach, so I am wondering what is modern, not declaring what is. At first glance it seems `protected` is a good use for member functions. – user3867053 Jul 23 '14 at 04:01
  • @user3867053, `friend` functions are not inherited, and inheritance is one of the building stones of OOP. Modify once, have all derived classes inherit the behaviour (virtual or non-virtual). With a friend, you are breaking this principle. – vsoftco Jul 23 '14 at 04:02
  • @vsoftco friend functions will work fine on instances of subclasses, and if the parent's implementation is unsatisfactory, you can overload them for the subclass. – user3867053 Jul 23 '14 at 04:04
  • @user3867053, ok, that is true, for public-type inheritance, but what if you want to hide the function in derived classes? And, lastly, it looks much more elegant to have data+functions_on_data coupled together in a `class`, instead of having a whole bunch of namespace-declared functions. In a sense, the `friend` approach is like using plain old `C` with `struct`s and functions operating on them. That's why `C++` was invented. – vsoftco Jul 23 '14 at 04:24
  • 1
    @user3867053, one more point. Eventually, whenever you have a huge class library (like STL for example), using a `friend` approach, you will end up with a HUGE number of friend functions, probably spread across a large number of files, and figuring out quickly which one is coupled to which becomes painful and error-prone. – vsoftco Jul 23 '14 at 04:29
  • @vsoftco I think it would work the same for protected/private inheritance: non-friends wouldn't be able to call the function overloaded for the parent on child instances, and friends would be able to (I think). – user3867053 Jul 23 '14 at 04:29
  • @vsoftco you would only have as many friend functions as you have member functions now, and they're not spread across multiple files now so I don't see a reason they would have to be with friends. So I'm not sure that's a valid reason. – user3867053 Jul 23 '14 at 04:31
  • @vsoftco if the sorting algorithm is incompatible, the sort method should probably be virtual. However for the sake of your example, the syntax `sort(obj)` will pick the overload that is most suited for `obj`. There is no issue there I think. If there is a problem, you have the same problem for member functions, because if you have a `A&` that refers to a `B` and you call `obj.sort()`, it will call `A::sort`, not `B::sort`. – user3867053 Jul 23 '14 at 04:47
  • @vsoftco `it looks much more elegant to have data+functions_on_data coupled together in a class, instead of having a whole bunch of namespace-declared functions` I disagree, this doesn't make sense for many operators and leads to bloated classes. OOP gobbledygook! – juanchopanza Jul 23 '14 at 05:09
  • @juanchopanza: I'd say the idiomatic approach is - where there aren't functional imperatives - to find a nice balance for these factors/concerns... a core of functionality at the logical level of operations on the type as member functions, and less-used, less-generic, added-dependency, multi-type, business- or app-related etc. functions as (friend if necessary) non-members. – Tony Delroy Jul 23 '14 at 05:35
  • @juanchopanza, I agree, one should have small classes and use loose coupling to achieve desired functionality, but for the small classes I prefer to have small number of member functions, instead of `struct` + friend functions. – vsoftco Jul 23 '14 at 20:22