调用专用模板函数时强制编译时错误

Force compile time error when specialized template function is invoked

本文关键字:编译时错误 函数 专用 调用      更新时间:2023-10-16

我有一个模板函数。只要实参不是指针类型,它就具有定义良好的语义。如果有人调用这个函数传递类型指针的参数,我想强制编译时错误。编写通用(合法)模板和相应的部分专门化(非法)版本没有问题。我只是不知道如何将错误从函数定义推迟到函数调用。

使用c++ 0x:(参见http://ideone.com/ZMNb1)

#include <type_traits>
#include <iostream>
template <typename T>
    void cannot_take_pointer(T ptr)
{
    static_assert(!std::is_pointer<T>::value, 
        "cannot_take_pointer requires non-pointer argument");
    std::cout << "okn";
}
int main()
{
    int x;
    cannot_take_pointer(x);
    cannot_take_pointer(&x);  // fails to compile
}

实际上你不需要专门化它。只需在函数体中添加以下内容:

BOOST_STATIC_ASSERT(!boost::is_pointer<T>()::value);

这将导致一个应该相当容易理解的错误

听起来像是boost::disable_if的完美案例。这样应该可以工作。

template <class T>
void func(T x, typename boost::disable_if<boost::is_pointer<T> >::type* dummy = 0) {
    std::cout << x << std::endl;
}

func(10); // works
func(std::string("hello")); // works
func("hello world"); // error: no matching function for call to 'func(const char [6])'
func(new int(10)); // error: no matching function for call to 'func(int*&)'

如果您想自己做这件事(而不是使用BOOstrongTATIC _ASSERT之类的东西),通常会涉及到两三个基本技巧。

第一个(可能是最重要的,在您的情况下)是使用sizeof(通常将结果转换为void)来编译一些代码,而不产生将在编译时执行的任何

第二种是生成一些在适当情况下是非法的代码。一个典型的方法是创建一个数组,它的大小等于某个表达式的值。如果表达式的值为0,则数组的大小为0,这是不允许的。或者,如果大小为1,则是合法的。这样做的一个问题是,它产生的错误消息通常是相当无意义的——很难猜测"error: array必须有正大小"(或类似的东西)与"模板形参不能是指针"有什么关系。

要产生更有意义的错误消息,通常使用一种稍微不同的技巧。在这种情况下,从一个类到另一个类的转换,如果表达式为假将失败,但如果表达式为真则成功。这样做的一种方法是:

template <bool>
struct check {  check(...);  };
template <>
class check<false> {};

check(...);意味着任何其他类型都可以(理论上)转换为check<true>(但请注意,我们只声明函数,从不定义它,所以如果您试图执行这样的代码,它将无法链接)。check<false>中没有any转换构造函数,这意味着试图将其他任何东西转换为check<false>总是会失败。

我们可以像这样在宏中使用它:

#define STATIC_ASSERT(expr, msg) {       
    struct Error_##msg {};               
    (void)sizeof(check<(expr)!=0>((Error_##msg))); 
}

你可以这样使用:STATIC_ASSERT(whatever, parameter_cannot_be_a_pointer);。它可以展开为:

struct Error_parameter_cannot_be_a_pointer {};
(void)sizeof(check<(expr)!=0>(Error_parameter_cannot_be_a_pointer);

然后,如果expr != 0,它将尝试将Error_parameter_cannot_be_a_pointer转换为check<true>,这将成功。

另一方面,如果expr 不等于0,它将尝试转换为check<false>,这将失败。我们至少希望当发生这种情况时,我们得到一个类似这样的错误消息:
 error cannot convert:
     Error_parameter_cannot_be_a_pointer
 to
     check<false>

显然,如果我们可以的话,我们想要一个更好的消息,但即使是这样,这也不是太糟糕。您只需要忽略"包装",并查看源类型的名称,就可以很好地了解问题所在。