Boost.Parameter:与CRTP结合使用的命名模板参数

时间:2012-06-28 19:04:39

标签: c++ templates crtp named-parameters boost-parameter

警告:提前说明需要解释问题。在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;
    };

上述代码允许通过命名BreadSlicerPolicy1_is来覆盖Policy2_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的旧post),我希望能够在命名模板参数上应用CRTP模式:< / p>

    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;
    }

但是,上面的Boost.Parameter实现无法编译,因为某些内部static_assert失败并显示消息,如(VC10 SP1)

  

'main :: CuriousBreadSlicer':不允许使用未定义的类作为   编译器内在类型特征'__is_base_of'

的参数

问题:可以关闭此静态检查吗?通过宏或模板技巧?

关于可能的解决方法:

  1. 上述代码在功能上等同于this handwritten code。对于该代码,CRTP模式确实有效。但是,它 Boost.Parameter库需要大量的样板代码才能方便 自动化。
  2. 我可以要求CRTP参数始终位于模板列表的第一位 参数而不是将它包装在Policy1_is类中。这解决了 编译时错误,但它失去了覆盖的顺序独立性。
  3. 所以看起来我是高尔夫球员所说的“俱乐部之间”。哪种解决方案最好?

1 个答案:

答案 0 :(得分: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<>模板。

当您使用该策略时,您可以检查它是否已包装,并将其解包。