1

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

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

'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 回答