non-intrusive で ad hoc な interface 定義の導入

http://d.hatena.ne.jp/Cryolite/20061213#p2
に関して。
あくまでストリーム入出力とlexical_castに限って、参照を保持するアダプタクラスを作成することを考えます。
次のような使用を想定します。

#include 

class hoge;
hoge func();

int main()
{
    hoge x;

    // パターン1
    std::cout << アダプタ(x) << std::endl;

    // パターン2
    std::cout << アダプタ(func()) << std::endl;

    // パターン3
    std::cin >> アダプタ(x);
}

パターン1は、出力後も参照先の実体が残っているため寿命の問題もなく、出力なのでconst参照でよいでしょう。


パターン2は、一時オブジェクトを受ける場合です。func()の戻り値の解体は、この行の式の評価の最終段階で行われるので、coutに出力する際の寿命は問題ありません。
パターン1と同様const参照になります。


パターン3は、寿命に関してはパターン1と同じですが、xへの書き込みが発生するため非constの参照が必要です。


パターン3だけ扱う参照の型が違うので素直に実装するとこうなります。

template<class T>
struct adaptor
{
    T& ref;

    explicit adaptor(T& x) : ref(x)
    {
    }
};

std::ostream& operator<<(std::ostream& os, const adaptor<const hoge>& a)
{
    // ...
}

std::istream& operator>>(std::istream& is, const adaptor<hoge>& a)
{
    // ...
}

ここまで考えてCryoliteさんのところにコメントを書いたわけですが、lexical_castを使うと問題が発生します。

hoge x;
std::string s = boost::lexical_cast<std::string>(adaptor<const hoge>(x)); // これはOK

x = boost::lexical_cast<adaptor<hoge> >(s).ref; // これは無理

問題は、

  • アダプタの保持している参照の対象オブジェクトはlexical_cast()の呼び出しから戻った時には解体されている
  • そもそもアダプタにデフォルトコンストラクタがないのでコンパイルできない

です。
やはり入力の場合には参照では駄目で実体が必要になります。

template<class T>
struct input_adaptor
{
    T value;
};

std::istream& operator>>(std::istream& is, input_adaptor<hoge>& a)
{
    // ...
}

int main()
{
    std::string s;
    hoge x = boost::lexical_cast<input_adaptor<hoge> >(s).value;
}

最後にvalueからxへのコピーが発生しますが、これはアダプタがなくても同じ事なので、ここでは別問題です。(move semantics要るかも、と思ったのはこの辺でした。)
入力と出力で全然違うクラスを使うのが格好悪いんですが、コスト的にはアダプタなしの状態とほぼ同じになると思います。


パターン3の使用も考慮して、adaptor<hoge>への入力も残しておいて、

template<class T>
std::istream& operator>>(std::istream& is, input_adaptor<T>& a)
{
    return is >> adaptor<T>(a.value);
}

としておくといいかも知れません。