【问题标题】:C++ metaprogramming - generating errors in codeC++ 元编程 - 在代码中生成错误
【发布时间】:2010-10-02 08:14:36
【问题描述】:

有没有一种方法可以创建一个接受 int 模板参数的函数,如果传递给函数的值小于 10,则让该函数给出编译时错误?

以下代码不起作用,但它显示了我想要完成的任务:

template <int number1>
void reportErrorIfLessThan10()
{
    #if(number1 < 10)
        #error the number is less than 10
    #endif
}


int maint(int argc, char**argv)
{
   reportErrorIfLessThan10<5>();//report an error!
   reportErrorIfLessThan10<12>();//ok
   return 0;
}

【问题讨论】:

    标签: c++ templates metaprogramming


    【解决方案1】:

    如果你不想要 Boost C++ Libraries 魔法并且想要裸露的骨头......

    template<bool> class static_check
    {
    };
    
    template<> class static_check<false>
    {
    private: static_check();
    };
    
    #define StaticAssert(test) static_check<(test) != 0>()
    

    然后使用静态断言。这对我来说是一个#define,因为我的代码需要在很多 C++ 不适用于模板的环境中运行,我需要将其退回到运行时断言。 :(

    另外,不是最好的错误消息。

    【讨论】:

    • 如果你不能使用模板,你总是可以像我发布的那样使用带有负子脚本的 typedef。比如,#define StaticAssert(test) typedef char static_assert ## __LINE__[(test) ? -1:1];
    【解决方案2】:

    如果由于某种原因你不能使用 Boost,这个例子可以简单地写成这样:

    template <int number1>
    void reportErrorIfLessThan10()
    {
        typedef char number1_gt_10[number1 > 10 ? 1 : -1];
    }
    
    
    int maint(int argc, char**argv)
    {
       reportErrorIfLessThan10<5>();//report an error!
       reportErrorIfLessThan10<12>();//ok
       return 0;
    }
    

    或者更通用的

    #define static_assert(test, message) typedef char static_assert_at_ ## __LINE__[(test) ? 1 : -1];
    

    我没有连接错误消息本身,因为我觉得static_assert(true, "some message");static_assert(true, some_message); 更具可读性。但是,这确实将用例限制为每行只有一个断言。

    【讨论】:

    • 数组边界必须移动到行尾。还有,分号。
    • 经过这么多年的 C++,我在做一个普通的 typedef 时仍然遇到了麻烦。呃。
    • 这个#define 代码对我来说失败了:编译器对错误分支进行语法检查。模板专业化方法效果更好。
    【解决方案3】:
    template <int number1>
    typename boost::enable_if_c< (number1 >= 10) >::type 
    reportErrorIfLessThan10() {
        // ...
    }
    

    上面的enable_if,没有_c,因为我们有一个普通的布尔值,看起来像这样:

    template<bool C, typename T = void>
    struct enable_if {
      typedef T type;
    };
    
    template<typename T>
    struct enable_if<false, T> { };
    

    Boostenable_if 不采用纯布尔值,因此他们有另一个附加了 _c 的版本,它采用纯布尔值。您将无法为 number1 SFINAE 将排除该模板作为可能的候选对象,因为如果条件评估为 @,enable_if 将不会公开类型 ::type 987654330@。如果你想,出于某种原因,在函数中测试它,那么如果你有C++1x 功能可用,你可以使用static_assert

    template <int number1>
    void reportErrorIfLessThan10() {
        static_assert(number >= 10, "number must be >= 10");
    }
    

    如果没有,你可以使用 BOOST_STATIC_ASSERT:

    template <int number1>
    void reportErrorIfLessThan10() {
        BOOST_STATIC_ASSERT(number >= 10);
    }
    

    不过,显示描述性消息的唯一方法是使用 static_assert。您可以或多或少地模拟这一点,使用具有描述错误条件的名称的类型:

    namespace detail {
        /* chooses type A if cond == true, chooses type B if cond == false */
        template <bool cond, typename A, typename B>
        struct Condition {
          typedef A type;
        };
    
        template <typename A, typename B>
        struct Condition<false, A, B> {
          typedef B type;
        };
    
        struct number1_greater_than_10;
    }
    
    template <int number1>
    void reportErrorIfLessThan10() {
        // number1 must be greater than 10
        sizeof( typename detail::Condition< (number1 >= 10), 
                 char, 
                 detail::number1_greater_than_10 
                >::type ); 
    }
    

    它在这里打印:

    错误:“sizeof”对不完整类型“detail::number1_greater_than_10”的无效应用

    但我认为第一种方法,使用enable_if 就可以了。您将收到关于未声明的reportErrorIfLessThan10 的错误消息。

    【讨论】:

      【解决方案4】:

      litb 和 Joe 已经给出了实践中使用的答案。只是为了说明如何通过专门针对有问题的数字(而不是一般的布尔条件)手动完成此操作:

      template <int N>
      struct helper : helper<N - 1> { };
      
      template <>
      struct helper<10> { typedef void type; };
      
      template <>
      struct helper<0> { }; // Notice: missing typedef.
      
      template <int N>
      typename helper<N>::type error_if_less_than_10() {
      }
      
      int main() {
          error_if_less_than_10<10>();
          error_if_less_than_10<9>();
      }
      

      函数不能被继承,但类(和结构)可以。因此,此代码还使用了一个结构,该结构自动动态地为除 10 和 0 之外的所有 N 生成案例,这是硬编码的递归开始。

      顺便说一句,上面的代码实际上给出了相当不错的错误信息:

      x.cpp:16: error: no matching function for call to 'error_if_less_than_10()'
      

      【讨论】:

      • 请记住,大多数编译器对您执行的递归深度都有限制。上次我检查时,GCC 默认为 50 左右。但是,我不知道这种类型的继承是否也是如此。
      • 嗯,这个测试是线性的。也就是说,您将获得从输入到 10 或 0 的每个 N 的实例化。因此,如果您设置 N=20000,您将获得 19990 个基类。我曾经在尝试创建太多模板实例时崩溃了我的操作系统。它至少需要 -ftemplate-depth-XXX :)
      • 是的,正如我所说,这段代码更多是为了说明而不是其他任何目的。没有理由(技术或其他方面)比其他解决方案更喜欢此解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-27
      • 2011-02-07
      • 1970-01-01
      • 2012-03-04
      相关资源
      最近更新 更多