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