10

カプセル化に関するキーワードの一般的な使用例は知っていますが、 「仕事を終わらせる」ためだけにキーワード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.

デフォルト値がコンストラクター宣言で指定される前に、コンパイラーがインターフェイス内のネストされたクラスの宣言を確認する方法はありますか?operator|Foo

friendまた、テンプレート内でネストされたクラスの対称操作を定義する際に、キーワードを使用していることに気付くこともあります。例えば:

template<typename T>
struct A{
  struct B{
    friend bool operator==(const B& x, const B& y) { return true; }  
  };  
};

operator==、カプセル化の観点から友情を必要としません。しかし、operator==実際にはテンプレート化された関数ではなく、コンパイラーがテンプレート内のネストされたクラスの型を推測できないため、これはoperator==フリー関数として保持する唯一の合理的な「トリック」のようです。

私が言ったように、これらの選択は機能しますが、より良い選択/実践があるかどうか疑問に思っています.

4

1 に答える 1

4

実際、それは完全に伝統的だと思います。Evg が言及したように、非表示のフレンドには特別なメリットがありますが、表示されているフレンドにもメリットがあります。

これをより多くの答えにするために、たとえば libstdc++ の の実装を検討してstd::unreachable_sentinel_tください。end()これは、 などの無制限の「ジェネレータ範囲」のとして返すことができますstd::ranges::iota_view{0}。2番目の例と非常によく似ています:

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{};

通常のイテレータとセンチネルは、ネストされたクラスとして定義され、フレンドシップにも依存していますが、それらの比較演算子には通常、特権アクセスが必要です。ただし、アウトオブラインの定義を提供できたとしても、テンプレートに友人をインライン化することの追加の利点は、潜在的に複雑な制約を含め、正確なテンプレート ヘッダーを繰り返す必要がないことです。

それが役立つ場合、C++ では、これらのようなフリー関数を、それらが友人であるかどうかにかかわらず、引数依存のルックアップにより、引数のクラスのパブリック インターフェイスの一部であると考えることができます。そのため、技術的に友情の特権を必要としない場合でも、それを付与してもカプセル化が解除されることはありません。

隠された友情のニュアンスを認識している限り、friendそれが仕事を成し遂げた場合にのみ使用してください!

于 2021-12-21T00:14:29.237 に答える