使用CRTP技术确保唯一的模板参数

时间:2012-08-02 22:01:18

标签: c++ template-meta-programming

来自boost::units

struct my_base_dimension1 : units::base_dimension<my_base_dimension1, 1> { };  // ok
struct my_base_dimension2 : units::base_dimension<my_base_dimension2, 2> { };  // ok
struct my_base_dimension3 : units::base_dimension<my_base_dimension3, 2> { };  // error

我试图理解代码如何确保模板参数是唯一的。我不明白check_base_dimension如何最终返回非零,触发ordinal_has_already_been_defined<true>。我认为这与在boost_units_is_registered()中重新定义base_dimension有关,但我不知道如何调用friend版本。有什么想法吗?

相关文件为base_dimension.hppprevent_redefinition.hpp

template<class Derived, long N,
         class = typename detail::ordinal_has_already_been_defined<
             check_base_dimension<Derived, N>::value
             >::type
         >
class base_dimension : public ordinal<N>
{
public:
    typedef base_dimension this_type;
    typedef list<dim<Derived,static_rational<1> >, dimensionless_type> dimension_type;
    typedef Derived type;

private:                                                                                                                           
    friend Derived*
    check_double_register(const units::base_dimension_ordinal<N>&)
        { return(0); }

    friend detail::yes
    boost_units_is_registered(const units::base_dimension_ordinal<N>&)
        { detail::yes result; return(result); }

    friend detail::yes
    boost_units_is_registered(const units::base_dimension_pair<Derived, N>&)
        { detail::yes result; return(result); }
};

1 个答案:

答案 0 :(得分:1)

啊哈,我相信我拥有它。

答案在本节中:

        /// Register this ordinal
        /// INTERNAL ONLY
        friend detail::yes 
        boost_units_is_registered(const units::base_dimension_ordinal&) 
        { return(detail::yes()); }

        /// But make sure we can identify the current instantiation!
        /// INTERNAL ONLY
        friend detail::yes 
        boost_units_is_registered(const units::base_dimension_pair&) 
        { return(detail::yes()); }

friend声明表示存在与这些参数匹配的函数,并返回detail::yes

当针对给定模板实例化enum中的check_base_dimension时,它会查找采用这两种类型的boost_units_is_registered函数。如果不存在这些模板参数的先前实例化,则会找到prevent_redefinition.hpp中定义的函数,该函数返回detail::no,但如果存在 ,则会找到函数的声明(friend)匹配那些返回detail::yes

的参数

重要的是要注意这都是在编译时,而不是运行时。编译器用户argument-dependent lookup找到匹配的函数。 sizeof该函数的结果仅取决于函数返回的内容 - 它根本不需要运行或调用,它只需要一个声明来给出返回值的大小。因此,当编译器找到friend函数时,它可以确定sizeof返回值然后 - 那里 - 函数实际上不需要定义。当然,如果您尝试使用它(如实际运行它),您将收到链接器错误,因为它已声明但从未定义过。

因此,sizeof()在编译时确定为detail::yes的大小,该大小与detail::no的大小不同。因此,表达式的结果为false,因此check_base_dimension::valuefalse,并且ordinal_has_already_been_defined的实例化不会获得名为type的成员变量。< / p>

编译器因此抛出一个错误,指出

detail::ordinal_has_already_been_defined<check_base_dimension<Derived, N>::value> does not have a member variable 'type'

或类似的。最后,实现了目标:您无法使用具有相同模板参数值的类的两个实例来编译代码。好哇!