【问题标题】:Boost.Parameter: named template argument in combination with CRTPBoost.Parameter:命名模板参数结合CRTP
【发布时间】:2012-06-30 08:42:22
【问题描述】:

警告:需要冗长的介绍来解释问题。在 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;
    };

上述代码允许通过命名Policy1_isPolicy2_is 以任意顺序覆盖BreadSlicer 的可选模板参数。这使得使用许多默认参数进行基于策略的设计非常方便。

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 模式:

    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 类中。这解决了 编译时错误,但它失去了覆盖的顺序独立性。

所以看起来我就是高尔夫球手所说的“在俱乐部之间”。哪种解决方案最好?

【问题讨论】:

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


    【解决方案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&lt;&gt; 模板。

    当你使用策略时,你可以检查它是否被包装,然后打开它。

    【讨论】:

    • CRTP 用于避免来自类型特征的 ODR。请参阅我在问题中链接的 Alexandrescu 的帖子。本质上,策略类用于强制所有特殊特征可见。你的包装想法很好。理想情况下,它应该是一个额外的参数,默认为false,将传递给boost::parameter::value_type&lt;ArgList, Key, Default, Wrapped=false&gt;
    • 命名模板参数似乎是有效的基于策略的编程的一个非常必要的需求。他们为什么没有进入标准?
    猜你喜欢
    • 1970-01-01
    • 2016-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-15
    • 1970-01-01
    相关资源
    最近更新 更多