【问题标题】:How do I validate template parameters in compile time when a templated class contains no usable member functions?当模板类不包含可用的成员函数时,如何在编译时验证模板参数?
【发布时间】:2011-04-22 07:45:49
【问题描述】:

我有以下模板struct

template<int Degree>
struct CPowerOfTen {
enum { Value = 10 * CPowerOfTen<Degree - 1>::Value };
};

template<>
struct CPowerOfTen<0> {
    enum { Value = 1 };
};

要这样使用:

const int NumberOfDecimalDigits = 5;
const int MaxRepresentableValue = CPowerOfTen<NumberOfDecimalDigits>::Value - 1;
// now can use both constants safely - they're surely in sync

现在模板要求Degree 为非负数。我想为此强制执行编译时断言。

我该怎么做?我试图给CPowerOfTen添加一个析构函数:

~CPowerOfTen() {
    compileTimeAssert( Degree >= 0 );
 }

但由于它不是直接调用的,Visual C++ 9 决定不实例化它,因此根本不评估编译时断言语句。

如何在编译时检查 Degree 是否为非负数?

【问题讨论】:

    标签: c++ visual-c++ templates metaprogramming


    【解决方案1】:
    template<bool> struct StaticCheck;
    template<> struct StaticCheck<true> {};
    
    template<int Degree> 
    struct CPowerOfTen : StaticCheck<(Degree > 0)> { 
        enum { Value = 10 * CPowerOfTen<Degree - 1>::Value }; 
    }; 
    
    template<> 
    struct CPowerOfTen<0> { 
        enum { Value = 1 }; 
    }; 
    

    编辑:没有无限递归。

    // Help struct
    template<bool, int> struct CPowerOfTenHelp;
    
    // positive case    
    template<int Degree> 
    struct CPowerOfTenHelp<true, Degree> { 
        enum { Value = 10 * CPowerOfTenHelp<true, Degree - 1>::Value }; 
    }; 
    
    template<> 
    struct CPowerOfTenHelp<true, 0> { 
        enum { Value = 1 }; 
    }; 
    
    // negative case
    template<int Degree> 
    struct CPowerOfTenHelp<false, Degree> {}
    
    // Main struct
    template<int Degree> 
    struct CPowerOfTen : CPowerOfTenHelp<(Degree >= 0), Degree> {};
    

    【讨论】:

    • 不停止编译时递归是个大问题?
    【解决方案2】:

    您可以使用 uint。您不会收到编译时错误,但至少它会是自记录的。

    【讨论】:

    • 没错。非负数只是一种失败。 CPowerOfTen 呢?这无论如何都不应该工作。
    • 永远不要使用 uint 来强制一个值是非负的。在这种情况下,如果传递 -1,它将被转换为一个巨大的值并作为模板参数传递,您的编译器将挂起,您将不知道该怎么做。幸运的是,即将推出的 C++0x 提供了带有自定义消息的静态断言,以便在断言失败的情况下进行报告
    【解决方案3】:

    您可以使用BOOST_STATIC_ASSERT 宏。或者实现你自己的,强制失败的最简单方法是执行一个包含 N 个元素的数组的 typedef,其中 N 是正数/负数,具体取决于参数。

    这种方法的问题在于它会产生失败,但仍会尝试执行递归。看看boost::enable_if_c,如果参数为负,则使用 SFINAE 无法实例化模板。

    【讨论】:

      【解决方案4】:

      您可以将实现转发给一个类,该类也接受一个布尔参数,指示是否可以计算结果。

      #include <limits>
      template <int Degree, bool InRange>
      struct PowerOfTenImpl
      {
          enum {Value = 10 * PowerOfTenImpl<Degree - 1, InRange>::Value};
      };
      
      template <>
      struct PowerOfTenImpl<0, true>
      {
          enum {Value = 1};
      };
      
      template <int Degree>
      struct PowerOfTenImpl<Degree, false>
      {
      };
      
      template<int Degree>
      struct CPowerOfTen {
          enum { Value = PowerOfTenImpl<Degree, Degree >= 0 && 
            Degree <= std::numeric_limits<int>::digits10>::Value };
      };
      
      int main()
      {
          const int a = CPowerOfTen<4>::Value;
          const int b = CPowerOfTen<1000>::Value;
          const int c = CPowerOfTen<-4>::Value;
      }
      

      【讨论】:

        【解决方案5】:

        实现一个STATIC_CHECK 宏怎么样?

        template<bool> struct CompileTimeError;
        template<> struct CompileTimeError<true> {}; //specialized only for true
        
        #define STATIC_CHECK(expr)  (CompileTimeError<(expr) != 0>())
        

        内部main()

         const int NumberOfDecimalDigits = -1;
         STATIC_CHECK(NumberOfDecimalDigits > 0); // Error : invalid use of incomplete type struct CompileTimeError<false>
        
         const int MaxRepresentableValue = CPowerOfTen<NumberOfDecimalDigits>::Value - 1; 
        

        【讨论】:

        • 这种方案的缺点是检查与struct是分开的。
        猜你喜欢
        • 2019-12-20
        • 1970-01-01
        • 2018-03-25
        • 2018-12-16
        • 1970-01-01
        • 1970-01-01
        • 2013-03-11
        • 2011-03-16
        • 1970-01-01
        相关资源
        最近更新 更多