C++元编程 - 在代码中生成错误

7

有没有一种方法可以创建一个函数,该函数接受一个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;
}
4个回答

8
如果您不想使用 Boost C++ Libraries 的高级功能,而只需要基础功能...
template<bool> class static_check
{
};

template<> class static_check<false>
{
private: static_check();
};

#define StaticAssert(test) static_check<(test) != 0>()

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

此外,错误信息不是最好的。


如果您不能使用模板,您始终可以使用带有负下标的typedef,就像我发布的那样。类似于#define StaticAssert(test) typedef char static_assert ## LINE[(test) ? -1 :1]。 - Jasper Bekkers

3
template <int number1>
typename boost::enable_if_c< (number1 >= 10) >::type 
reportErrorIfLessThan10() {
    // ...
}

上述的enable_if,没有_c因为我们有一个简单的bool,看起来像这样:
template<bool C, typename T = void>
struct enable_if {
  typedef T type;
};

template<typename T>
struct enable_if<false, T> { };

Boostenable_if不接受普通的bool类型,因此他们有另一个版本,加上了_c后缀,可以接受普通的bool类型。您将无法为number1<10调用它。SFINAE将排除该模板作为可能的候选项,因为如果条件评估为falseenable_if将不会公开类型::type。如果您想在函数中测试它,那么如果您有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 ); 
}

它在这里打印出来:
错误:对不完整类型“detail :: number1_greater_than_10”的'sizeof'的无效应用
但我认为第一种方法,使用enable_if会做到这一点。您将收到有关未声明的reportErrorIfLessThan10的错误消息。

3

如果由于某些原因您无法使用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);更易读。然而,这限制了每行只能使用一个断言的用例。


数组边界必须移动到行末。另外,需要加上分号。 - Konrad Rudolph
在这么多年的C++编程经验中,我仍然很难使用普通的typedef。唉。 - Jasper Bekkers
这个 #define 代码对我来说失败了:编译器会对错误分支进行语法检查。模板特化方法效果更好。 - A Fog

0

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>();
}

函数不能被继承,但类(和结构体)可以。因此,这段代码还使用了一个结构体,它会自动动态生成所有N的情况,除了10和0,它们是硬编码递归开始。

顺便说一下,上面的代码实际上提供了相当好的错误信息:

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

请记住,大多数编译器对递归深度有限制。上次我检查时,GCC 默认为50左右。但是,我不知道这种继承是否也适用相同的限制。 - Jasper Bekkers
那个测试是线性的。也就是说,你将会得到从输入的N值一直到10或0的每一个实例化。所以如果你设置N=20000,你将会得到19990个基类。我曾经试图创建太多模板的实例化而导致操作系统崩溃了。至少需要使用-ftemplate-depth-XXX :) - Johannes Schaub - litb
是的,正如我所说,这段代码更多是为了说明而不是其他任何目的。没有任何理由(技术或其他方面)优先选择此解决方案而不是其他解决方案。 - Konrad Rudolph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接