Friend and Inline Method, What's the Point

friend AND inline method, what's the point ?

friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs) { 
return(lhs.fVal==rhs.fVal);
}

is sometimes called friend definition, because it is a friend declaration that also defines the function. It will define the function as a non-member function of the namespace surrounding the class it appears in. Actually, the inline there is redundant: It's implicitly declared inline if it's a friend definition. Some pros and cons of it:

  • It makes the operator not visible to normal lookup. The only way you can call it is using argument dependent look-up. This will keep the namespace free of lots of operator declarations visible normally. Note that this will also disable the ability of calling it using implicit conversions to MonitorObjectString (because if both argument types do not match at the time of looking for candidates to be called, argument dependent look-up won't find the function).
  • The lookup for names starts in the scope of the class the friend definition appears in. This means that no long type-names or other names need to be written out. Just refer them as you would in a normal member function of the class.
  • As it is a friend, the function sees the internals of MonitorObjectString. But that's neither good nor bad. It depends on the situation. For example if there are functions getFVal() making the function friend is pretty pointless. Could use getFVal as-well then.

I used to like this friend definition style of operators, because they have direct access to class members, and appear within the class definition - so I could have "everything with one sight". Recently, however, I came to the conclusion that it's not always a good idea. If you can (and you should) implement the operator purely using public member functions of the class, you should make it a non-friend (and non-member) operator, defined in the same namespace of the class. It makes sure that if you change some implementation - but keep the interface of the class the same - the operator will still work and you have less cascading changes, because you know it can't access implementation details.

However, I prefer this style over writing member operators, because operator functions at namespace scope have the added features of being symmetric with their arguments: They don't treat the left side special, because both sides are just normal arguments and not object arguments that are bound to *this. If either the left or the right side is of the type of your class, the other side can be implicitly converted - regardless of whether it's left or right. For functions that are also defined without the friend definition syntax (traditionally, at namespace scope), you will have the feature of selectively including headers that make those operators available or not.

Inline friend function defined in class body

Yes. See [class.friend/6-7] from the Standard:

A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8), the function name is unqualified, and the function has namespace scope.

Such a function is implicitly inline.

What's the scope of inline friend functions?

When you declare a friend function with an unqualified id in a class it names a function in the nearest enclosing namespace scope.

If that function hasn't previously been declared then the friend declaration doesn't make that function visible in that scope for normal lookup. It does make the declared function visible to argument-dependent lookup.

This is emphasised in many notes, but the definitive statement is in 7.3.1.2/3 (of ISO/IEC 14882:2011):

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

Is ADL the only way to call a friend inline function?

Is it true, then, that such an inline friend function can only be called with argument-dependant lookup?

Yes. As specified in [namespace.memdef]/3:

If a friend declaration in a non-local class first declares a class,
function, class template or function template. the friend is a member
of the innermost enclosing namespace. The friend declaration does not
by itself make the name visible to unqualified lookup
([basic.lookup.unqual]) or qualified lookup ([basic.lookup.qual]).

Since the only declaration of f is its inline definition, it's not made visible to qualified or unqualified lookup. ADL however, has a special provision for such friend functions, [basic.lookup.argdep]/4:

When considering an associated namespace, the lookup is the same as
the lookup performed when the associated namespace is used as a
qualifier ([namespace.qual]) except that:

  • Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective
    namespaces even if they are not visible during an ordinary lookup
    ([class.friend]).

As for your bonus question, a lambda should do it:

auto exposed_g = [](S const& s){ g(s); };

It wraps the ADL into its body. Though the usual caveats about return type deduction apply. It will be a value (assuming you don't return void).

Is a friend function defined in-class automatically inline?

Yes, it is. §11.4/5:

A function can be defined in a friend
declaration of a class if and only if
the class is a non-local class (9.8),
the function name is unqualified, and
the function has namespace scope.
Such a function is implicitly inline. A friend function defined in
a class is in the (lexical) scope of
the class in which it is defined. A
friend function defined outside the
class is not (3.4.1).

Since the class definition is presumably in a header file, the function will be multiply-defined, so it needs to be inline.

Difference between a pointer to a standalone and a friend function

What I'm missing?

An inline friend declaration does not make the function available to ordinary name lookup.

Pay close attention to the error. It does not say that the function is of the wrong type, it simply can't find anything named operator==. This is by design.

Inline friend definitions are only found by argument dependent lookup. Ordinary lookup (such as naming the function to take its address), cannot find it. If you want the function to be available for that purpose, you must provide a namespace scoped declaration.

class X {
public:
friend bool operator==(int, X const &) { /* ... */ }
};

bool operator==(int, X const &);

Why is this friend method not found as expected?

It comes down to how C++ generates candidate functions when performing overload resolution. It's trying to find candidates for operator<<(std::cout, b). This means it performs unqualified name lookup which includes performing argument-dependent lookup (ADL). Let's take a look at how that works.

For the first code snippet, unqualified name lookup finds the declaration when it looks in the enclosing scope of the calling code, without needing to perform ADL. It sees inline std::ostream& operator<<(std::ostream& os, const A&) as a candidate, and then is able to apply the user-defined conversion to b to see that it's a valid function to use for overload resolution. All well and good.

For the second code snippet, though, we don't have a declaration of operator<< at file scope. The declaration and definition are entirely within the definition of the class A. That still might let us find it as a candidate function for std::cout << b, but it'll have to be through ADL. Let's check to see if it's actually visible through that:

Otherwise, for every argument in a function call expression its type
is examined to determine the associated set of namespaces and classes
that it will add to the lookup.

...


  1. For arguments of class type (including union),
    the set consists of

a) The class itself

b) All of its direct and indirect base classes

c) If the class is a member of another class, the class of which it is a member

d) The innermost enclosing namespaces of the classes added to the set

At any stage, would we look inside the definition of A when performing ADL with arguments std::cout and b? None of a), b), and c) apply to A because A isn't B, A isn't a base class of B, and A doesn't contain B as a member. Crucially, "any class to which the class is implicitly convertible" isn't used to generate candidates through ADL.

So ultimately in the second code snippet, the name lookup never sees the declaration of std::ostream& operator<<(std::ostream& os, const A&) and never realizes that it can apply a user-defined conversion to apply it with the appropriate arguments.

If we just make the function declaration (not definition) visible at file scope like so:

#include <iostream>

class A {
public:
friend std::ostream& operator<<(std::ostream& os, const A&) {
os << "Called\n";
return os;
}
};

std::ostream& operator<<(std::ostream& os, const A&);

class B {
public:
operator A() { return A(); }
};

int main()
{
A a;
std::cout << a;
B b;
std::cout << b;
}

This function declaration is once again found through ordinary unqualified name lookup, the user-defined conversion comes in during overload resolution, and the expected output of "Called" being printed twice is recovered.



Related Topics



Leave a reply



Submit