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

原创 2007年09月13日 12:35:00

  

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>”

一个经典的问题(构造函数调用+抽象类+间接继承抽象类)

经典案例 案例1: using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

关于基类指针指向子类对象,构造函数和析构函数调用的顺序的问题。

类中析构函数与构造函数的调用顺序问题。

关于C++派生类中构造函数调用顺序的问题

近期在网上搜寻了一下关于C++派生类构造函数的调用问题,大部分博文给出的顺序是: 1、基类构造 2、成员构造 3、派生构造。 这个顺序严格来讲是错误的,对新手极具误导性!     依据侯捷翻译...

关于基类构造函数调用虚函数实际调用的不是派生类的问题的原因

博客已迁移至:http://kulv.sinaapp.com/ 关于基类构造函数调用虚函数实际调用的不是派生类的问题的原因   我们知道,类的构造函数里面编译器插入了很多代码,比如异...

C++中一个关于复制构造函数和指针的问题

Copy构造函数是显式地去处理一个对象初始化另一个对象的问题,如果类含有一个指针的话,那么处理起来主要分为两种情况。 A. 指针在类的构造或者初始化时指向动态分配的内存, 并在对象析构时对该指针进行...
  • nemoyy
  • nemoyy
  • 2016年03月17日 13:11
  • 832

C++中类的构造函数调用顺序

当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的构造函数,依次类推,直至到达派生类次数最多的派生次数最多的类的构造函数为止。简而言之,对象是由“底层向上”开始构造的。因为,构造函数一开...

关于父类与子类的构造函数调用顺序

关于父类与子类的构造函数调用顺序

java父类构造函数调用子类覆盖方法

原文链接:http://blog.csdn.net/zhuoaiyiran/article/details/19489745 参考:http://blog.csdn.net/bettarwang/a...

C++中类的构造函数调用顺序

C++子类和父类的构造函数调用顺序 #include using namespace std; //子类 class base { public: base() { cout...

C++虚继承(七) --- 虚继承对基类构造函数调用顺序的影响

继承作为面向对象编程的一种基本特征,其使用频率非常高。而继承包含了虚拟继承和普通继承,在可见性上分为public、protected、private。可见性继承比较简单,而虚拟继承对学习c++的难度较...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一个将类的构造函数调用识别成函数指针的问题
举报原因:
原因补充:

(最多只允许输入30个字)