1

警告:問題を説明するために、先に長い紹介が必要です。VandevoordeとJosuttisの16.1章で最初に説明された名前付きテンプレート引数イディオムは、Boost.Parameterライブラリを使用して簡単に記述できます。

    #include <iostream>
    #include <typeinfo>
    #include <boost/parameter.hpp>
    #include <boost/static_assert.hpp>

    struct DefaultPolicy1 {};
    struct DefaultPolicy2 {};

    typedef boost::parameter::void_ DefaultSetter;

    BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy1_is)
    BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy2_is)

    typedef boost::parameter::parameters<
            boost::parameter::optional<tag::Policy1_is>,
            boost::parameter::optional<tag::Policy2_is>
    > PolicySelector;

    template
    <
            class PolicySetter1 = DefaultSetter,
            class PolicySetter2 = DefaultSetter
    >
    class BreadSlicer
    {
            typedef typename PolicySelector::bind<
                    PolicySetter1, 
                    PolicySetter2
            >::type Policies;

    public:
            // extract policies:
            typedef typename boost::parameter::value_type<
                    Policies, tag::Policy1_is, DefaultPolicy1
            >::type P1;

            typedef typename boost::parameter::value_type<
                    Policies, tag::Policy2_is, DefaultPolicy2
            >::type P2;
    };

上記のコードを使用すると、のオプションのテンプレートパラメータに名前を付けて任意の順序でオーバーライドできBreadSlicerます。これにより、多くのデフォルトパラメータを使用したポリシーベースの設計が非常に便利になります。Policy1_isPolicy2_is

int main()
{
        typedef BreadSlicer<> B1;

        // can override any default policy
        typedef BreadSlicer< Policy1_is<int> > B2;
        typedef BreadSlicer< Policy2_is<char> > B3;

        // order of policy-setting is irrelevant
        typedef BreadSlicer< Policy1_is<int>, Policy2_is<char> > B4;
        typedef BreadSlicer< Policy2_is<char>, Policy1_is<int> > B5;

        // similar static asserts work for B1 ... B4     
        BOOST_STATIC_ASSERT((std::is_same<B5::P1, int >::value));
        BOOST_STATIC_ASSERT((std::is_same<B5::P2, char>::value));

        return 0;
}

ポリシーベースの設計での非常に微妙なODR違反を回避するために(説明については、Alexandrescuによるこの古い投稿を参照してください)、名前付きテンプレート引数にCRTPパターンを適用できるようにしたいと思います。

    int main() 
    {
            // ERROR: this code does NOT compile!
            struct CuriousBreadSlicer
            :
                    BreadSlicer< Policy1_is<CuriousBreadSlicer> >
            {};

            typedef CuriousBreadSlicer B6;

            BOOST_STATIC_ASSERT((std::is_same<B6::P1, CuriousBreadSlicer>::value));
            BOOST_STATIC_ASSERT((std::is_same<B6::P2, DefaultPolicy2    >::value));

            return 0;
    }

ただし、一部の内部static_assertが(VC10 SP1)のようなメッセージで失敗するため、上記のBoost.Parameter実装はコンパイルに失敗します。

'main :: CuriousBreadSlicer':コンパイラの組み込み型特性'__is_base_of'への引数として未定義のクラスは許可されていません

質問:この静的チェックをオフにすることはできますか?マクロまたはテンプレートのトリックのいずれかを介して?

考えられる回避策について:

  1. 上記のコードは、この手書きコードと機能的に同等です。そのコードでは、CRTPパターンが機能します。ただし、Boost.Parameterライブラリが便利に自動化する多くの定型コードが必要です。
  2. CRTPパラメーターを常にテンプレート引数のリストの最初に配置し、 Policy1_isクラスでラップしないようにすることができます。これにより、コンパイル時のエラーは解決されますが、オーバーライドの順序に依存しなくなります。

ですから、私はゴルフ選手が「クラブの間」と呼んでいるもののようです。どのソリューションが最適ですか?

4

1 に答える 1

1

CRTPを使用しない最小限の例:

#include <boost/parameter.hpp>
#include <boost/static_assert.hpp>

BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy1_is)
BOOST_PARAMETER_TEMPLATE_KEYWORD(Policy2_is)

typedef boost::parameter::parameters<
           boost::parameter::optional<tag::Policy1_is>,
           boost::parameter::optional<tag::Policy2_is>
        > PolicySelector;


struct foo {};
struct bar {};
struct baz;
typedef typename PolicySelector::bind<foo, baz>::type Policies;
boost::parameter::value_type<Policies, tag::Policy1_is, bar>::type x; // <- !!!

したがってboost::parameter::value_type、ポリシーセレクターは完全な型に基づいている必要がありますが、手書きのクラスには当てはまりません。

クラスが独自のポリシーとしてそれ自体を必要とする理由は完全にはわかりません。それが必要な場合は、不完全なタイプを完全なものでラップすることができます。

struct CuriousBreadSlicer : BreadSlicer <
          Policy1_is<CuriousBreadSlicer *> > // <- compiles

wrap_incomplete_type<>または、わかりやすくするために、独自のテンプレートを使用することもできます。

ポリシーを使用すると、ラップされているかどうかを確認したり、ラップを解除したりできます。

于 2012-06-30T09:45:25.873 に答える