use*_*364 10 c++ templates operator-overloading friend non-member-functions
我知道关键字在封装方面的一般用例,friend但有几次,我需要关键字friend只是为了“完成工作”。这些用例并不让我高兴,所以我想知道是否有一些替代方案。这是第一个最小的例子:
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.
Run Code Online (Sandbox Code Playgroud)
在构造函数声明中给出默认值之前,编译器有什么方法可以查看接口内部嵌套类的声明吗?operator|Foo
我有时还发现自己使用friend关键字来定义模板内嵌套类的对称操作。例如:
template<typename T>
struct A{
struct B{
friend bool operator==(const B& x, const B& y) { return true; }
};
};
Run Code Online (Sandbox Code Playgroud)
operator==从封装的角度来看,不需要友谊。但由于operator==实际上并不是模板化函数,并且编译器无法推断模板内嵌套类的类型,这似乎是保留为operator==自由函数的唯一合理的“技巧”。
正如我所说,这些选择确实有效,但我想知道是否有更好的选择/做法。
事实上,我想说这是完全传统的。正如 Evg 所提到的,隐藏的朋友有特殊的好处,可见的朋友也很棒!
为了使这个问题更像一个答案,请考虑 libstdc++ 的std::unreachable_sentinel_t. 这可以作为无界“生成器范围”的 来返回end(),例如std::ranges::iota_view{0}. 它与您的第二个示例非常相似:
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{};
Run Code Online (Sandbox Code Playgroud)
常规迭代器和哨兵被定义为嵌套类,并且也依赖于友谊,尽管它们的比较运算符通常确实需要特权访问。但是,即使您可以提供外线定义,在模板中内联友元的另一个好处是您不必重复确切的模板标头,包括潜在的复杂约束。
如果有帮助的话,在 C++ 中,由于依赖于参数的查找,您可以将此类自由函数(无论它们是否是友元)视为其参数类的公共接口的一部分。因此,即使您在技术上不需要友谊特权,授予它也不会破坏封装。
只要您了解隐藏友谊的细微差别,只要friend它能完成工作就使用它!