C++ 中朋友的非常规用法

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==自由函数的唯一合理的“技巧”。

正如我所说,这些选择确实有效,但我想知道是否有更好的选择/做法。

sig*_*gma 4

事实上,我想说这是完全传统的。正如 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它能完成工作就使用它!