首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

内联函数,该如何处理

2013-01-01 
内联函数本帖最后由 zhaoomeng 于 2012-12-06 20:09:38 编辑请看以下4个程序 (1)inline void facts(){}int

内联函数
本帖最后由 zhaoomeng 于 2012-12-06 20:09:38 编辑 请看以下4个程序 
(1)inline void facts(){}
     int main()
     {
        void facts();
         facts();
      }

(2)inline void facts(){}
     void facts();
     int main()
     {
        facts();
      }

(3)void facts();
     int main()
     {
        facts();
      }
      inline void facts(){}

(4) int main()
      {
         void facts();
         facts();
       }
       inline void facts(){}

其中为什么唯独(4)出错呢?

错误信息如下: error LNK2019: 无法解析的外部符号 "void __cdecl facts(void)" (?facts@@YAXXZ),该符号在函数 _main 中被引用(VS2012)

我知道内联函数应当定义于头文件的规定,我只是很想知道(4)不合法的原因?


[解决办法]
这是 declaration scope, unqualified name lookup, 以及 inline function definition 联合作用的结果。

具体到 (4) 的情况,void facts (); 出现在 main 函数里面,因此这个 facts 只具有 block scope,用人的话说这个 facts 就是 main-facts, 而非 ::facts,因此在 (4) 里面 facts 的调用点执行 unqualified name lookup 的结果是找到了 main-facts,而不是 ::facts,后者还不存在呢。后面 inline facts 定义的时候,facts 才出现为 global namespace scope,不过标准没要求 inline function 的定义一定存在,3.2/3 说 

An inline function shall be defined in every translation unit in which it is odr-used.

这句话只说 odr-used inline function 一定要有定义,没说没用到的是否要有定义,因此编译器可以自由发挥了。
关键是这里的 inline function 没用被 odr-used,因为调用点的 facts 被决议为 main-facts,而非 ::facts,即编译器不认为 main-facts 和 ::facts 是一个东西。因此从编译器的角度讲,::facts 根本就没有用到(odr-used),所以也不需要有定义,因此 inline facts 压根就没产生对应的代码。然后编译器的事儿完了,链接器接手,结果这回郁闷了,因为调用点说我需要跳转到一个叫 facts 的函数,但是 linker 发现这函数压根就不存在(因为inline定义没有生成代码),所以 linker 就报错。

(3) 是正确的,因为调用点的 facts 决议为 ::facts,因此编译器知道后面的 inline function 在 main 里面调用了,所以是 odr-used 了,所以必须生成代码,因此木有问题了。

(4) 改成这样也没有问题


int main()
{
 void facts();
 facts();
}
void facts(){}

因为这回 facts 不是 inline 的,编译器没有自由,必须为其生成代码,所以后面 linker 进来的时候就 ok 了。不过我觉得严格来说,这种用法是 undefined behavior,因为 linker happy 完全是因为 main{void facts();} 不经过复杂的 name mangling,后面的 ::facts {} 也是保留了朴素的名字,因此 linker 通过字串比较刚刚好得到了匹配,能够生成跳转。但这是没有保证的,因为在更复杂的情况下,main-facts 和 facts 经 mangling 以后就不一定一样了,这时候还会失败。关键点就是按 (4) 的声明方式 main-facts 和 ::facts 不是一个东西,这点可以通过以下程序验证。

int main()
{
 void facts();
 facts();
}
namespace mangle
{


 void facts(){}
}
using namespace mangle;


此时 mangle::facts 的名词不再是 facts,这回 linker 就不那么幸运了,字串匹配的把戏不好使了,还得报错。
[解决办法]
证明一下吧。以下代码vc 2010和g++ 4.2通过编译。

//仅供测试,不要在工程中只用这样的手法。
int main()
{
void facts();
//估计此处, 编译器可否inline,如何inline,所以多半没有inline
facts();
return 0;
}

inline void facts(){}

//防止编译器在obj中删除inline函数facts
 void dummy(void (* &pf)())
{
pf = &facts;
 }

热点排行