I know the general use cases for the friend
keyword with regards to encapsulation but one a couple of occasions, I have needed the friend
keyword just to "get the job done". These use cases don't make me happy so I'm wondering if there are some alternatives. Here's the first minimal example:
struct Foo{
enum class Bar{
a=1,b=2,c=4
};
// need to tell the compiler of operator| before it gets used
// but it can't be a member function of Foo: so add friend keyword
friend Bar operator|(const Bar& b1, const Bar& b2);
// constructor needs a default value using
// operator| for Bars
Foo( Bar b = Bar::a | Bar::b );
};
// definition of operator|, etc.
Is there any way for the compiler to see the declaration of the operator|
for the nested class, inside of interface, before the default values are given at the Foo
constructor declaration?
I also sometimes find myself using the friend
keyword in defining symmetric operations of nested classes within templates. For example:
template<typename T>
struct A{
struct B{
friend bool operator==(const B& x, const B& y) { return true; }
};
};
The operator==
does not require friendship from an encapsulation perspective. But due to operator==
not actually being a templated function and the compiler being unable to deduce the types of nested classes within templates, this seems to be the only reasonable "trick" to keep operator==
as a free function.
As I said, these choices do work, but I'm wondering if there are better choices/practices out there.
A friend function is a function that isn't a member of a class but has access to the class's private and protected members.
Friend Class A friend class can access private and protected members of other class in which it is declared as friend. It is sometimes useful to allow a particular class to access private members of other class. For example, a LinkedList class may be allowed to access private members of Node.
A friend class is a class that can access the private and protected members of a class in which it is declared as friend. This is needed when we want to allow a particular class to access the private and protected members of a class.
In fact, I would say it's perfectly conventional. While, as mentioned by Evg, hidden friends have special benefits, visible friends are also great to have!
To make this more of an answer, consider for example libstdc++'s implementation of std::unreachable_sentinel_t
. This can be returned as the end()
of an unbounded "generator range", such as a std::ranges::iota_view{0}
. It's very similar to your second example:
struct unreachable_sentinel_t
{
template<weakly_incrementable _It>
friend constexpr bool
operator==(unreachable_sentinel_t, const _It&) noexcept
{ return false; }
};
inline constexpr unreachable_sentinel_t unreachable_sentinel{};
Regular iterators and sentinels are defined as nested classes and also rely on friendship, though their comparison operators typically do need the privileged access. However, even if you could provide an out-of-line definition, an additional benefit of inlining friends in templates is that you don't have to repeat the exact template header, including potentially complicated constraints.
If it helps, in C++ you can think of free functions like these, whether or not they are friends, as being part of the public interface of the classes of their arguments, due to argument-dependent lookup. As such, even if you don't technically need the privilege of friendship, granting it does not break encapsulation.
As long as you're aware of the nuance of hidden friendship, just use friend
if it gets the job done!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With