Base-from-Member慣用法

9038 ワード

Base-from-Member慣用法


クラステンプレートboost::base_from_memberは、1つのメンバーでベースクラスを初期化する必要があるクラスに解決策を提供する.このクラステンプレートはboost/utility/base_にあります.from_member.hpp、このファイルはboost/utility.hppには.
テストおよびインスタンスコードはbase_にあります.from_member_test.cpp.

目次

  • ディレクトリ
  • 原理
  • 要約
  • 用法
  • Credits
  • Contributors


  • げんり


    クラスを開発する場合、ベースクラスは現在のクラスのメンバーによって初期化される必要がある場合があります.この例のように
    #include <streambuf>  // for std::streambuf
    #include <ostream>    // for std::ostream
    
    class fdoutbuf
        : public std::streambuf
    {
    public:
        explicit fdoutbuf( int fd );
        //...
    };
    
    class fdostream
        : public std::ostream
    {
    protected:
        fdoutbuf buf;
    public:
        explicit fdostream( int fd )
            : buf( fd ), std::ostream( &buf )
            {}
        //...
    };
      

    これは、C++の初期化順序では、ベースクラスが使用するメンバーの前に初期化する必要があるため、定義されていません.R.Samuel Klatchkoは初期化順序を利用した解決法を与える.ベースクラスは宣言の順序で初期化されるので、関連するメンバーを別のベースクラスに移動し、元のベースクラスより前に初期化すれば、正しい初期化が保証されます.
    この方法でカスタマイズしたベースクラスは次のとおりです.
    #include <streambuf>  // for std::streambuf
    #include <ostream>    // for std::ostream
    
    class fdoutbuf
        : public std::streambuf
    {
    public:
        explicit fdoutbuf( int fd );
        //...
    };
    
    struct fdostream_pbase
    {
        fdoutbuf sbuffer;
    
        explicit fdostream_pbase( int fd )
            : sbuffer( fd )
            {}
    };
    
    class fdostream
        : private fdostream_pbase
        , public std::ostream
    {
        typedef fdostream_pbase  pbase_type;
        typedef std::ostream     base_type;
    
    public:
        explicit fdostream( int fd )
            : pbase_type( fd ), base_type( &sbuffer )
            {}
        //...
    };
      

    他のプロジェクトでは、同様のカスタムベースクラスも使用できます.このテクニックは非常に基本的で、テンプレートで提供すべきです.主なテンプレートパラメータは、含まれるメンバーのタイプです.このテンプレートクラスには、コンストラクション関数のパラメータをメンバーに渡すためのコンストラクション関数メンバーテンプレートがいくつかあります.このテンプレートクラスでは、暗黙的なレプリケーション構造と付与操作が使用され、含まれているメンバーがレプリケーションできない場合はキャンセルされます.
    テンプレートを適用するクラスが複雑な構造とレプリケーション操作を必要としたり、コンパイラがあまり先進的ではなく、あまり使用しない場合は、ベースクラスを手動で作成するほうがいいです.
    ベースクラスには名前がないため、1つのクラスに同じタイプのベースクラスを複数(直接)持つことはできません.このテンプレートクラスには、異なる個別のクラスを提供するための追加のテンプレートパラメータがあります.このパラメータにはデフォルト値があり、メンバータイプが1つしかない場合は、パラメータを1つだけ書くことができます.

    サマリ

    #ifndef BOOST_BASE_FROM_MEMBER_MAX_ARITY
    #define BOOST_BASE_FROM_MEMBER_MAX_ARITY 10
    #endif

    template < typename MemberType, int UniqueID = 0 >
    class boost::base_from_member
    {
    protected:
    MemberType member;

    base_from_member();

    template< typename T1 >
    explicit base_from_member( T1 x1 );

    template< typename T1, typename T2 >
    base_from_member( T1 x1, T2 x2 );

    //...

    template< typename T1, typename T2, typename T3, typename T4,
    typename T5, typename T6, typename T7, typename T8, typename T9,
    typename T10 >
    base_from_member( T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7,
    T8 x8, T9 x9, T10 x10 );
    };

    このクラステンプレートの最初のテンプレートパラメータMemberTypeはベースクラスのメンバとしてのタイプを表す.別のテンプレートパラメータUniqueIDは、ベースクラスとして複数の同じタイプを使用することを区別する整数です.次のテンプレートパラメータが無視された場合、デフォルト値は0です.このクラステンプレートには、memberという保護データメンバーがあり、派生クラスを後続のベースクラス(または派生クラス自体)に使用することができます.
    デフォルトのコンストラクション関数といくつかのコンストラクション関数メンバーテンプレートがあります.これらのコンストラクション関数テンプレートは、複数のパラメータ(現在最大10個)を受け入れ、データ・メンバーに渡すコンストラクション関数です.C++ではテンプレートコンストラクション関数のテンプレートパラメータを明示的に宣言することはできませんので、これらのパラメータがデータ・メンバーのコンストラクション関数で使用される実際のタイプにできるだけ近いことを確認します.
    BOOST_BASE_FROM_MEMBER_MAX_ARITYマクロ定数は、コンストラクション関数テンプレートの最大パラメータ長を指定します.この定数は、より多くの(またはより少ない)パラメータ構成が必要な場合に上書きできます.この定数は拡張可能なコードで読み出され、同じ最大値を維持する必要があります.(たとえば、クラスはクラステンプレートをベースクラスとして使用し、メンバーは柔軟な構造関数のセットを持っています).

    使用法


    最初の例では、fdoutbufのサブオブジェクトは、1つのベースクラスにカプセル化され、std::ostreamの前に継承される.
    #include <boost/utility/base_from_member.hpp>

    #include <streambuf> // for std::streambuf #include <ostream> // for std::ostream class fdoutbuf : public std::streambuf { public: explicit fdoutbuf( int fd ); //... }; class fdostream : private boost::base_from_member<fdoutbuf> , public std::ostream { // Helper typedef's typedef boost::base_from_member<fdoutbuf> pbase_type; typedef std::ostream base_type; public: explicit fdostream( int fd ) : pbase_type( fd ), base_type( &member ) {} //... };

    base-from-member慣用は実装の詳細であるため、fdostreamのユーザー(または派生クラス)に表示されるべきではありません.初期化順序のため、fdoutbufサブオブジェクトはstd::ostreamサブオブジェクトより前に初期化され、これにより前のサブオブジェクトは、後のサブオブジェクトの構造において安全に使用される.最終タイプのfdoutbufサブオブジェクトは、名前「member」を持つ唯一のサブオブジェクトであるため、この名前は最終クラスに限定されずに使用することができる.


    base-from-memberクラステンプレートには、stream-bufferをI/Oストリームに関連付けるようなbase-from-memberサブオブジェクトが通常1つしか含まれません.次の例では、複数のbase-from-memberサブオブジェクトをどのように使用するか、および使用を限定するかの問題を示します.
    #include <boost/utility/base_from_member.hpp>

    #include <cstddef> // for NULL struct an_int { int y; an_int( float yf ); }; class switcher { public: switcher(); switcher( double, int * ); //... }; class flow_regulator { public: flow_regulator( switcher &, switcher & ); //... }; template < unsigned Size > class fan { public: explicit fan( switcher ); //... }; class system : private boost::base_from_member<an_int> , private boost::base_from_member<switcher> , private boost::base_from_member<switcher, 1> , private boost::base_from_member<switcher, 2> , protected flow_regulator , public fan<6> { // Helper typedef's typedef boost::base_from_member<an_int> pbase0_type; typedef boost::base_from_member<switcher> pbase1_type; typedef boost::base_from_member<switcher, 1> pbase2_type; typedef boost::base_from_member<switcher, 2> pbase3_type; typedef flow_regulator base1_type; typedef fan<6> base2_type; public: system( double x ); //... }; system::system( double x ) : pbase0_type( 0.2 ) , pbase1_type() , pbase2_type( -16, &this->pbase0_type::member ) , pbase3_type( x, static_cast<int *>(NULL) ) , base1_type( pbase3_type::member, pbase1_type::member ) , base2_type( pbase2_type::member ) { //... }

    最終クラスには複数のサブオブジェクトに名前「member」が付いているため、この名前を使用する場合は1つのベースクラス名で限定する必要があります(typedefを使用するとベースクラス名をより容易に参照できます).しかし、これはポインタを使用する必要がある場合に新しい問題をもたらします.クラス名で定義されたサブオブジェクトにアドレスオペレータを使用すると、メンバーを指すポインタではなく、メンバーポインタ(タイプan_int boost::base_from_member<an_int, 0> :: *)が得られます(タイプan_int *).この新しい問題の解決策は、「this->」でステータオブジェクトを制限することであり、これはポインタに必要であり、参照または値には必要ない.
    初期化にはいくつかのパラメータの変換があります.pbase0_typeの構造関数のパラメータはdoubleからfloatに変換する.pbase2_typeの構造関数の第1のパラメータはintからdoubleに変換する.pbase3_type構造関数の第2のパラメータは、変換を必要とする特殊な状況である.C++のすべての形式の空のポインタはコンパイル期間の整数式と見なされるため、C++は常にこのようなコードを整数として解釈し、整数またはポインタの再ロードを受け入れることができる場合は、常に整数として解釈します.コンパイラでは、switcherのコンストラクタにポインタタイプを使用する正しいコンストラクタを呼び出すには、最後の変換が必要です.

    Credits


    Contributors


    Ed Brey
    Suggested some interface changes.
    R. Samuel Klatchko ( [email protected], [email protected])
    Invented the idiom of how to use a class member for initializing a base class.
    Dietmar Kuehl
    Popularized the base-from-member idiom in his IOStream example classes.
    Jonathan Turkanis
    Supplied an implementation of generating the constructor templates that can be controlled and automated with macros. The implementation uses the Preprocessor library.
    Daryle Walker
    Started the library. Contributed the test file
    base_from_member_test.cpp.
    Revised: 28 August 2004
    Copyright 2001, 2003, 2004 Daryle Walker. Use, modification, and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or a copy at .)