最終更新日時(UTC):
が更新

履歴 編集

可変引数テンプレートでfriend宣言をできるようにする [P2893R3]

このページはC++26に採用される見込みの言語機能の変更を解説しています。

のちのC++規格でさらに変更される場合があるため関連項目を参照してください。

概要

C++26では、可変引数テンプレートのパラメータパックを、friend宣言に指定できるようになる。

template <class... Ts>
class Foo {
  friend Ts...; // C++26: OK
};

この指定により、パラメータパックTs内のすべての型をfriend指定したことになる。

Passkeyイディオム

Passkeyイディオムを使用すると、メンバ関数ごとにアクセス許可をだすことができる。以下のプログラムでは、クラスCはクラスAにfriend関係を付与している。そのため、A::m()Cの内部構造すべてにアクセスできる。

しかし、CはPasskeyイディオムを使用して、intentionalA()intentionalB()へのアクセスも許可している。Passkey<B>クラスには、フレンド関係のBのみがアクセスできるプライベートコンストラクタがあるため、BのみがPasskey<B>のインスタンスを作成できる。最初の引数としてPasskey<B>のインスタンスを指定せずにintentionalB()を呼び出すことはできない。そのため、intentionalB()は公開メンバ関数ではあるが、Bからのみ呼び出すことができる。

template<class T>
class Passkey {
  friend T;
  Passkey() {}
};

class A;
class B;

class C {
  friend A;
private:
  void internal();
public:
  void intentionalA(Passkey<A>);
  void intentionalB(Passkey<B>);
};

class A {
  void m(C& c) {
    c.internal();       // OK
    c.intentionalA({}); // OK
    c.intentionalB({}); // エラー!Passkey<B>のコンストラクタにアクセスできない
  }
};

class B {
  void m(C& c) {
    c.intentionalB({}); // OK
  }
};

C++26ではこのイディオムを拡張し、複数のクラスに同時にアクセス許可を付与できるようになる。

template<class... Ts>
class Passkey {
  friend Ts...;
  Passkey() {}
};

class C {
public:
  // Blarg, Blip, Bazクラスからのみ呼び出せる
  void intentional(Passkey<Blarg, Blip, Baz>);
};

派生クラスへのCRTPアクセス

クラステンプレートから継承する際、派生クラスの型を基底クラスのテンプレート引数として指定するイディオムが「CRTP (Curiously Recurring Template Pattern、奇妙に再帰したテンプレートパターン)」と呼ばれている。このイディオムでも、可変引数テンプレートでfriend宣言できるようになることが有用である。

派生クラスの一部APIが基底クラスで必要となる場合があり、基底クラスでのみ必要であるためそのAPIはprivateにして、基底クラスにのみアクセスを許可したいことがある。そういった状況で、C++26での可変引数テンプレートでのfriend宣言が必要になる。

template<class Crtp, class MsgT>
class Receiver {
  void receive(MsgT) {
    static_cast<Crtp*>(this)->private_ += 1;
  }
};

template<class... MsgTs>
struct Dispatcher :
  public Receiver<Dispatcher<MsgTs...>, MsgTs>... // OK。可変引数テンプレートでの継承
{
  using Receiver<Dispatcher, MsgTs>::Receiver...;  // OK。可変引数テンプレートでのusing宣言
  friend Receiver<Dispatcher, MsgTs>...; // C++26: OK

private:
  int private_;
};

関連項目

参照