boost::operatorsとprivate継承とfriendの話

boost:operatorsっていうのは「operator==の結果を否定したものを返す」という内容でoperator!=を定義してくれるライブラリで(かなりはしょっている)、operator==を定義するだけで!=も使えるようになるのでラクチンです。くわしくはこちら:letsboost::operators

でも、その使い方のサンプルを見るとなぜかprivate継承をしているのです。private継承をすると親クラスのメンバはすべてprivateになります。つまり継承したものは外から全然見えません。なぜこれで動くの?

と疑問に思ったのでboost/operators.hppを読んでみました。

        • -

たとえばこんなクラスFooがあるとします。

class Foo : private Addable<Foo>{};

下のようなコードを書いて、コンパイルエラーにならないためにはどういう風にAddableを定義すればいいか?

#include <iostream>
using namespace std;

int main(){
  Foo x;
  cout << x + 1 << endl;
}

で、試しに実装してみたのがこれ。

template<typename T>
struct Addable{
  int friend operator+(T&, int x){
    return x;
  }
};

これでコンパイルが通るようになります。キモは「friend関数はクラスの中で定義してもかまわない。その関数のスコープは、そのクラス定義を取り囲むスコープに輸出される」(C++プライマー第4版 P.511)という点。つまりこれは一見メソッドのように見えるけども、実際はクラスには属していないわけです。なのでそもそもprivate継承でいいのかどうか以前に、親から何も受け継いでいないわけです。

ついつい「継承しているから、親クラスから何か必要なものをもらうんだ」と思ってしまいましたが、ここでのAddableの継承は単に「Fooを型引数にしてAddableを実体化」というコンパイル時処理の引き金を引くためだけにあるようです。その証拠に下のように書き換えてもgcc4.0でコンパイルできます。

class Foo{};
class Bar : private Addable<Foo>{};

ただ、これはgcc4.2.3だとコンパイルエラーになってしまいます。僕はgcc4.2.3でコンパイルしようとしてダメだったので、上で書いたような理解は間違いなのか??とサイボウズラボの掲示板に書いてみたところ、奥さん、光成さん、山本さんとamachangとで話が盛り上がってものすごく勉強になったのでした。ありがとうございます。でも掲示板がソースコード込みで40件くらい流れててとてもまとめきれないので詳細は割愛(ぉ


see Barton-Nackman trick - Wikipedia, the free encyclopedia (via @SubaruG)