警告:前面需要冗长的介绍来解释问题。在 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_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 的这篇旧帖子),我希望能够在命名模板参数上应用 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' 的参数
问题:这个静态检查可以关闭吗?通过宏或模板技巧?
至于可能的解决方法:
- 上面的代码在功能上等同于这个手写代码。对于该代码,CRTP 模式确实有效。然而,它需要大量的样板代码,Boost.Parameter 库可以方便地自动化。
- 我可以要求 CRTP 参数在模板参数列表中始终排在第一位,而不是将其包装在一个
Policy1_is
类中。这解决了编译时错误,但它失去了覆盖的顺序独立性。
所以看起来我就是高尔夫球手所说的“在俱乐部之间”。哪种解决方案最好?