Free Function Versus Member Function

Is there any advantage to implementing functions as free functions rather than members in C++?

This answer may helps you : Operator overloading : member function vs. non-member function?. In general free functions are mandatory if you need to implement operators on classes you don't have access to code source (think about streams) or if left operand is not of class type (int for example). If you control the code of the class then you can freely use function members.

For your last question, no, function members are uniquely defined and an object internal table is used to point to them. Function members can be viewed as free functions with an hidden parameter that is a pointer to the object, i.e. o.f(a) is more or less the same as f(&o,a) with a prototype roughly like f(C *this,A a);.

C++ Member Functions vs Free Functions

The Interface Principle by Herb Sutter

For a class X, all functions, including free functions, that both

(a) "mention" X, and

(b) are "supplied with" X

are logically part of X, because they form part of the interface of X.

For in depth discussion read Namespaces and the Interface Principle by Herb Sutter.

EDIT

Actually, if you want to understand C++ go and read everything what Herb Sutter has written :)

Which takes precedence, the free function operator==() or the member function operator==()?

I ran the code a couple times and it seems that the member operator wins out.

Yes, the member wins in your example. But not for the reason you think.

Member functions have an implicit object parameter (what this points to), and the type of the object parameter is determined by the cv-qualifiers at the end of the member function. In this case, your member operator has no cv-qualifiers, so the type of the implicit object is simply A.

Basically, we have two candidates:

bool operator==(A const&, B const&); // your non-member operator
bool operator==(A&, B const&); // your member operator

The member operator is a better match because the first parameter is a better match - we don't have to take a more const-qualified reference to a.

If you had made your operator const (as you generally should), then we'd have these two candidates:

bool operator==(A const&, B const&); // your non-member operator
bool operator==(A const&, B const&); // better member operator

Which are identical, there is no reason to prefer one to the other, and we'd get an ambiguity. There is no rule to prefer a member to a non-member function (or vice versa).

Should the operators == and = in C++ 20 be implemented as a member or a free function?

I would argue that in C++20, comparisons should be member functions unless you have a strongly compelling reason otherwise.

Lemme first start with the C++17 calculus: we would often write our comparisons as non-members. The reason for this is that it was the only way to allow two-sided comparisons. If I had a type X that I wanted to be comparable to int, I can't make 1 == X{} work with a member function - it has to be a free function:

struct X { };
bool operator==(X, int);
bool operator==(int lhs, X rhs) { return rhs == lhs; }

There wasn't much choice in the matter. Now, writing these as purely free functions is sub-optimal because we're polluting the namespace and increasing the amounts of candidates in lookup - so it's better to make them hidden friends:

struct X {
friend bool operator==(X, int);
friend bool operator==(int lhs, X rhs) { return rhs == lhs; }
};

In C++20, we don't have this issue because the comparisons are themselves symmetric. You can just write:

struct X {
bool operator==(int) const;
};

And that declaration alone already allows both X{} == 1 and 1 == X{}, while also already not contributing extra candidates for name lookups (it will already only be a candidate if one side or the other is an X).

Moreover, in C++20, you can default comparisons if they're declared within the declaration of the class. These could be either member functions or hidden friends, but not external free functions.


One interesting case for a reason to provide non-member comparison is what I ran into with std::string. The comparisons for that type are currently non-member function templates:

 template<class charT, class traits, class Allocator>
constexpr bool
operator==(const basic_string<charT, traits, Allocator>& lhs,
const basic_string<charT, traits, Allocator>& rhs) noexcept;

This has importantly different semantics from making this a member (non-template) function or a hidden friend (non-template) function in that it doesn't allow implicit conversions, by way of being a template. As I pointed out, turning this comparison operator into a non-template would have the effect of suddenly allowing implicit conversions on both sides which can break code that wasn't previously aware of this possibility.

But in any case, if you have a class template and want to avoid conversions on your comparisons, that might be a good reason to stick with a non-member function template for your comparison operator. But that's about it.

Why do generic programming designs prefer free functions over member functions?

The main motivation is that preferring nonmember nonfriend functions helps keep classes as concise as possible. See Herb Sutter's article here: http://www.gotw.ca/publications/mill02.htm.

This article also contains the answer to the other part of your question, where to put the corresponding get function. It's a feature of C++ called Argument Dependent Lookup (ADL). From Herb Sutter, who calls it Koenig lookup although this name is controversial (see comments below):

Koenig lookup says that, if you supply a function argument of class
type, then to find the function name the
compiler is required to look, not just in the usual places like the
local scope, but also in the namespace (here NS) that contains the
argument's type.

Here's an example:

namespace MyNamespace {
class MyClass {... };
void func(MyClass);
}

int main(int aArgc, char* aArgv[]) {
MyNamespace::MyClass inst;
func(inst); // Ok, because Koenig says look in the argument's namespace for func
}

So in short, you just declare the get function in the same namespace as your class.

Be aware that this doesn't work for templated functions if you have to supply the template parameters explicitly -- see this post: https://stackoverflow.com/a/2953783/27130



Related Topics



Leave a reply



Submit