我有一个模板功能。只要参数不是指针类型,它就具有明确定义的语义。如果有人调用此函数并传递类型指针的参数,我想强制编译时错误。我可以轻松编写通用(合法)模板和相应的部分专业化(非法)版本。我只是不知道如何将错误从函数定义推迟到函数调用。
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
}
实际上,您不需要专门化它。只需将其添加到您的函数体中:
BOOST_STATIC_ASSERT(!boost::is_pointer<T>()::value);
这将导致失败,这应该很容易理解。
如果您想自己执行此操作(而不是使用诸如 BOOST_STATIC _ASSERT 之类的东西),通常会涉及到两个或三个基本技巧。
第一个(在您的情况下可能也是最重要的)是使用sizeof
(通常将结果转换void
为 )来编译一些代码,而不会产生任何将在编译时执行的东西。
第二个是在适当的情况下产生一些非法的代码。一种典型的方法是创建一个大小等于某个表达式的值的数组。如果表达式的值为 0,则数组的大小为 0,这是不允许的。或者,如果大小为 1,则它是合法的。这样做的一个问题是它产生的错误消息通常毫无意义——很难猜测“错误:数组必须具有正大小”(或类似的东西)与“模板参数不能是指针”有什么关系.
为了产生更有意义的错误消息,您通常使用稍微不同的技巧。在这种情况下,从一个类到另一个类的转换,如果表达式为假则失败,但如果为真则成功。一种方法是这样的:
template <bool>
struct check { check(...); };
template <>
class check<false> {};
这check(...);
意味着任何其他类型都可以(理论上)转换为 a check<true>
(但请注意,我们只声明函数,从不定义它,因此如果您尝试执行此类代码,它不会链接)。缺少任何转换构造函数check<false>
意味着尝试将其他任何东西转换为 acheck<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 转换为 a check<true>
,这将成功。
另一方面,如果expr
do等于 0,它将尝试转换为 a check<false>
,这将失败。我们至少希望当这种情况发生时,我们会收到类似这样的错误消息:
error cannot convert:
Error_parameter_cannot_be_a_pointer
to
check<false>
显然,如果可以的话,我们希望得到比这更好的信息,但即便如此,这也不算太糟糕。您只需忽略“包装”,并查看源类型的名称即可很好地了解问题。
听起来像是一个完美的案例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*&)'