ぼやきごと/2011-09-15/テンプレートクラスとその派生クラスに対する特殊化の適用 のバックアップの現在との差分(No.1)


  • 追加された行はこの色です。
  • 削除された行はこの色です。
#blog2navi()
*テンプレートクラスとその派生クラスに対する特殊化の適用 [#p7480397]
LEFT:Prev: [[[派生クラスに対するテンプレート特殊化の適用>ぼやきごと/2011-09-14/派生クラスに対するテンプレート特殊化の適用]]]

[[前回の記事>ぼやきごと/2011-09-14/派生クラスに対するテンプレート特殊化の適用]]において、あるクラスとその派生クラスに対してテンプレートの特殊化を適用させる方法を示しました。~
継承元が普通のクラスである場合は [[boost ライブラリ>http://www.boost.org/]]を用いることで容易に実現可能でした。

では、継承元がテンプレートクラスである場合はどうでしょうか。~
例えば次のような場合です。

#code(c){{
#include <boost/type_traits/is_base_of.hpp>
#include <boost/utility/enable_if.hpp>
#include <iostream>

//----------

//! Base<T> クラス
template<class T>
class Base { };

//! Base<T> クラスの派生クラス
template<class T>
class Derived : public Base<T> { };

//! Base<bool> クラスの派生クラス
class DerivedBool : public Base<bool> { };

//----------

//! 関数オブジェクトテンプレート(基本型)
template<class T, class = void>
struct Func
{
    void operator()() const
    {
        std::cout << "Normal" << std::endl;
    }
};

// Base<T> クラス(型 T は任意)から派生するあらゆるクラスに
// 適用するにはどう特殊化すればいい??
/*
#if 0
template<class T>
struct Func< T, ??? >
{
    void operator()() const
    {
        std::cout << "Special" << std::endl;
    }
};
*/
#endif

//----------

int main(int, char**)
{
    Func<int>            a; a();    // Normal を期待
    Func< Base<char> >   b; b();    // Special を期待
    Func< Derived<int> > c; c();    // Special を期待
    Func<DerivedBool>    d; d();    // Special を期待

    return 0;
}
}}

解法は(私の頭では)2種類考え付きます。

@code{Base<T>}; クラスが変更可能である場合は簡単で、 @code{Base<T>}; クラスを更に別のテンプレートでないクラスから継承させれば済みます。~
継承の大元がテンプレートクラスでなくなってしまえば、[[前回の記事>ぼやきごと/2011-09-14/派生クラスに対するテンプレート特殊化の適用]]の手法がそのまま使えます。

#code(c){{
#include <boost/type_traits/is_base_of.hpp>
#include <boost/utility/enable_if.hpp>
#include <iostream>

//----------

//! BaseT クラス
class BaseT { };

//! Base<T> クラス
template<class T>
class Base : public BaseT { };

//! Base<T> クラスの派生クラス
template<class T>
class Derived : public Base<T> { };

//! Base<bool> クラスの派生クラス
class DerivedBool : public Base<bool> { };

//----------

//! 関数オブジェクトテンプレート(基本型)
template<class T, class = void>
struct Func
{
    void operator()() const
    {
        std::cout << "Normal" << std::endl;
    }
};

//! BaseT クラスとその派生クラス用に部分特殊化した Func
template<class T>
struct Func<
    T,
    typename boost::enable_if< boost::is_base_of<BaseT, T> >::type >
{
    void operator()() const
    {
        std::cout << "Special" << std::endl;
    }
};

//----------

int main(int, char**)
{
    Func<int>            a; a();    // Normal を期待
    Func< Base<char> >   b; b();    // Special を期待
    Func< Derived<int> > c; c();    // Special を期待
    Func<DerivedBool>    d; d();    // Special を期待

    return 0;
}
}}

@code{Base<T>}; クラスが変更不可能である場合、[[前回の記事>ぼやきごと/2011-09-14/派生クラスに対するテンプレート特殊化の適用]]の最初の解法でも用いた手法である「可変長引数関数による型の切り分け」を行う必要があります。~
boost ライブラリのメタプログラミング手法に準じた書き方をすると次のようになります。

#code(c){{
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/add_reference.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/detail/yes_no_type.hpp>
#include <boost/utility/enable_if.hpp>
#include <iostream>

//----------

//! Base<T> クラス
template<class T>
class Base { };

//! Base<T> クラスの派生クラス
template<class T>
class Derived : public Base<T> { };

//! Base<bool> クラスの派生クラス
class DerivedBool : public Base<bool> { };

//----------

namespace
{
    //! TDerived 型が TBase<T> 型およびその派生型か否かのチェッククラス
    template<class TDerived>
    class BaseTDerivedChecker
    {
    private:
        //! チェック用可変長引数関数
        static boost::type_traits::no_type check(...);

        //! Base<T> 型およびその派生型の場合のみ定義されるオーバロード
        template<class T>
        static boost::type_traits::yes_type check(
            const Base<T>&,
            typename boost::enable_if<
                boost::is_base_of<Base<T>, TDerived> >::type* = 0);

        //! チェック用静的メンバ変数
        static typename boost::add_reference<TDerived>::type d;

    public:
        //! チェック結果
        static const bool value =
            (sizeof(check(d)) == sizeof(boost::type_traits::yes_type));
    };
}

//! TDerived 型が TBase<T> 型およびその派生型か否か調べるメタ関数
//! boost::enable_if 等に渡して使える
template<class TDerived>
struct IsBaseTDerived
    :
    public boost::integral_constant<
        bool,
        BaseTDerivedChecker<TDerived>::value>
{
};

//----------

//! 関数オブジェクトテンプレート(基本型)
template<class T, class = void>
struct Func
{
    void operator()() const
    {
        std::cout << "Normal" << std::endl;
    }
};

//! Base<T> クラスとその派生クラス用に部分特殊化した Func
template<class T>
struct Func<
    T,
    typename boost::enable_if< IsBaseTDerived<T> >::type >
{
    void operator()() const
    {
        std::cout << "Special" << std::endl;
    }
};

//----------

int main(int, char**)
{
    Func<int>            a; a();    // Normal を期待
    Func< Base<char> >   b; b();    // Special を期待
    Func< Derived<int> > c; c();    // Special を期待
    Func<DerivedBool>    d; d();    // Special を期待

    return 0;
}
}}

@code{BaseTDerivedChecker<TDerived>}; テンプレートクラスでは、少なくとも1つの型 @code{T}; について型 @code{TDerived}; が次の要件を満たす場合のみ静的メンバ関数 @code{check}; のオーバロードが定義されます。

- 型 @code{TDerived}; の参照型が型 @code{const Base<T>&}; に変換可能である。
- 型 @code{TDerived}; が @code{Base<T>}; クラスまたはその派生クラスである。

いずれの型 @code{T}; についても型 @code{TDerived}; が上述の条件を満たさない場合、オーバロードは定義されません。

なお、[[前回の記事>ぼやきごと/2011-09-14/派生クラスに対するテンプレート特殊化の適用]]も含め、例示したコードは古いコンパイラではコンパイルエラーとなる可能性があります。~
私自身は Visual C++ 2008 でコンパイルして動作確認しています。

RIGHT:Category: &#x5b;[[C++>ぼやきごと/カテゴリ/C++]]&#x5d;&#x5b;[[boost>ぼやきごと/カテゴリ/boost]]&#x5d;&#x5b;[[プログラミング>ぼやきごと/カテゴリ/プログラミング]]&#x5d; - 2011-09-15 00:14:03
----
RIGHT:&blog2trackback();
#comment(above)
#blog2navi()