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

时间:2011-11-12 20:40:12

标签: c++ templates compiler-errors partial-specialization

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

4 个答案:

答案 0 :(得分:4)

使用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 << "ok\n";
}

int main()
{
    int x;
    cannot_take_pointer(x);
    cannot_take_pointer(&x);  // fails to compile
}

答案 1 :(得分:2)

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

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

这将导致一个相当容易理解的失败。

答案 2 :(得分:1)

如果你想自己这样做(而不是像BOOST_STATIC _ASSERT这样使用),通常会涉及两到三个基本技巧。

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

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

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

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

template <>
class check<false> {};

check(...);表示任何其他类型可以(理论上)转换为check<true>(但请注意,我们只声明该函数,从不定义它,所以如果你试图执行这样的代码,它不会链接)。 check<false>中缺少任何转换构造函数意味着尝试将其他任何内容转换为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>

显然,如果我们可以,我们希望得到一个更好的信息,但即便如此,这也不是太可怕。你只需要忽略“包装”,并查看源类型的名称以便很好地了解问题。

答案 3 :(得分:1)

听起来像是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*&)'