关闭

一个将类的构造函数调用识别成函数指针的问题

标签: 编译器structc++classc
1170人阅读 评论(1) 收藏 举报
分类:

  

Modern C++ Design第二章第一节,有一个关于定制编译器错误提示的例子:

template<bool> struct CompileTimeChecker 
{
    CompileTimeChecker(...);    
//一个接受任何参数的构造函数
};
template
<> struct CompileTimeChecker<false> { };//特化false版本 


//定义一个宏,msg为用户传入的错误消息提示,expr为判断式,expr为false时编译期将出
//错,并显示错误信息。
#define STATIC_CHECK(expr, msg)/
{/
    
class ERROR_##msg{};/
    (
void)sizeof(CompileTimeChecker<(expr) != 0>(ERROR_##msg()));/
}

有了这个宏STATIC_CHECK后,可以用来实现一个安全的指针类型转换模板函数,它检查目标类型的大小,如果目标类型小于原始类型,则在产生一个编译期错误,并显示我们自定义的错误提示:

template<typename To,typename From>
To Safe_reinterpret_cast(From 
&from)
{
    STATIC_CHECK(
sizeof(From) <= sizeof(To),
                  Destination_Type_Too_Narrow);
//定义错误提示信息
    return reinterpret_cast<To>(from);//如果宏验证通过表示可以安全转换
};
int main()
{
    
int *pa=0;
    
char c=Safe_reinterpret_cast<char>(pa);//将int指针转换成为char,根
                  // 据Safe_reinterpret_cast的定义,这里将不能通过编译
    return 0;
}

 

VC6.0下编译,提示:Error: cannot convert ERROR_Destination_Type_Too_Narrow to CompileTimeChecker<false>,我们定制的提示消息已经出现在Error中了。

 
这是个小技巧,CompileTimeChecker<true>这个特化类有一个可接受任何参数的的构造函数,这意味着当宏STATIC_CHECKexpr参数表达式在编译期评估为true时,(void)sizeof(CompileTimeChecker<(expr) != 0>(ERROR_##msg()))就有效,相当于调用CompileTimeChecker<true>接受一个类ERROR_##msg对象作为参数的构造函数
 
如果expr结果为false时,就会有编译期的错误发生:编译器找不到类CompileTimeChecker<false>接受一个参数的构造函数,从而显示错误信息。
 
这个STATIC_CHECK宏不会生成任何对象或指令造成运行时的开销,所有的检查都是在编译期完成。并且还能定制错误提示以方便找错,相对于晦涩难懂的模板编译错误提示,这可算得上非常不错了。
 
但是,当我将这些代码移植到VS2005却出现了问题,提示信息如下:
error C2066: 转换到函数类型是非法的
error C2070: CompileTimeChecker<false>
( Safe_reinterpret_cast::ERROR_Destination_Type_Too_Narrow (__cdecl *)(void) )

分析了一下错误信息,错误出在这一句:

(void)sizeof(CompileTimeChecker<(expr) != 0>(ERROR_##msg()));

原来编译器并没有按照我们的意思将ERROR_##msg()解释成调用类ERROR_##msg的默认构造函数来生成一个临时对象当作实参。却错误的将其解释成一个函数指针,一个没有参数,返回类型为ERROR_##msg类型的函数指针ERROR_Destination_Type_Too_Narrow (__cdecl *)(void)

 
为了解决这个问题,我们需要提供给编译器更多的信息,以使编译器能够正确的用ERROR_##msg()生成一个临时对象。
一个解决方法是用类名作限定符显示的调用构造函数:
(void)sizeof(CompileTimeChecker<(expr) != 0>(ERROR_##msg::ERROR_##msg()));
 
另一个方法是加上一个取地址操作符,因为取地址运算符只能做用于对象,这样编译器就知道ERROR_##msg()构造的是一个对象而不是定义一个函数指针了。
(void)sizeof(CompileTimeChecker<(expr) != 0>(&ERROR_##msg()));//这样就OK
现在提示信息变成:
法从”Safe_reinterpret_cast::ERROR_Destination_Type_Too_Narrow *__w64转换为”CompileTimeChecker<false>”
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:15982次
    • 积分:313
    • 等级:
    • 排名:千里之外
    • 原创:15篇
    • 转载:0篇
    • 译文:0篇
    • 评论:6条
    文章分类
    文章存档
    最新评论