我正在使用一个特定于MS的关键字来强制全局函数被内联,但是我注意到如果它使用一个具有明确的简单析构函数的对象,该函数就无法内联。
引自MSDN
即使使用
__forceinline
,编译器也无法内联代码 情况。如果出现以下情况,编译器无法内联函数:
函数或其调用者使用
/Ob0
编译(调试版本的默认选项)。函数和调用者使用不同类型的异常处理(C ++异常处理在一个结构化异常处理中 在另一个)。
该函数有一个可变参数列表。
该函数使用内联汇编,除非使用
/Og
,/Ox
,/O1
或/O2
进行汇编。该函数是递归的,不附带
#pragma inline_recursion(on)
。使用pragma,递归函数被内联到16个调用的默认深度。要减少内联深度,请使用inline_depth
编译指示。该功能是虚拟的,虚拟调用。可以内联直接调用虚函数。
程序获取函数的地址,并通过指向函数的指针进行调用。可以内联直接调用已经获取其地址的函数。
该功能也标有裸
__declspec
修饰符。
我正在尝试以下自包含程序来测试行为
#include <iostream>
#define INLINE __forceinline
template <class T>
struct rvalue
{
T& r_;
explicit INLINE rvalue(T& r) : r_(r) {}
};
template <class T>
INLINE
T movz(T& t)
{
return T(rvalue<T>(t));
}
template <class T>
class Spam
{
public:
INLINE operator rvalue<Spam>() { return rvalue<Spam>(*this); }
INLINE Spam() : m_value(0) {}
INLINE Spam(rvalue<Spam> p) : m_value(p.r_.m_value) {}
INLINE Spam& operator= (rvalue<Spam> p)
{
m_value = p.r_.m_value;
return *this;
}
INLINE explicit Spam(T value) : m_value(value) { }
INLINE operator T() { return m_value; };
template <class U, class E> INLINE Spam& operator= (Spam<U> u) { return *this; }
INLINE ~Spam() {}
private:
Spam(Spam<T>&); // not defined
Spam& operator= (Spam&); // not defined
private:
T m_value;
};
INLINE int foo()
{
Spam<int> p1(int(5)), p2;
p2 = movz(p1);
return p2;
}
int main()
{
std::cout << foo() << std::endl;
}
使用简单的析构函数INLINE ~Spam() {}
,我们有以下反汇编
int main()
{
000000013F4B1010 sub rsp,28h
std::cout << foo() << std::endl;
000000013F4B1014 lea rdx,[rsp+30h]
000000013F4B1019 lea rcx,[rsp+38h]
000000013F4B101E mov dword ptr [rsp+30h],5
000000013F4B1026 call movz<Spam<int> > (013F4B1000h)
000000013F4B102B mov rcx,qword ptr [__imp_std::cout (013F4B2050h)]
000000013F4B1032 mov edx,dword ptr [rax]
000000013F4B1034 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013F4B2040h)]
000000013F4B103A mov rdx,qword ptr [__imp_std::endl (013F4B2048h)]
000000013F4B1041 mov rcx,rax
000000013F4B1044 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013F4B2058h)]
}
如果没有析构函数INLINE ~Spam() {}
,我们将进行以下反汇编
int main()
{
000000013FF01000 sub rsp,28h
std::cout << foo() << std::endl;
000000013FF01004 mov rcx,qword ptr [__imp_std::cout (013FF02050h)]
000000013FF0100B mov edx,5
000000013FF01010 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013FF02040h)]
000000013FF01016 mov rdx,qword ptr [__imp_std::endl (013FF02048h)]
000000013FF0101D mov rcx,rax
000000013FF01020 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013FF02058h)]
}
000000013FF01026 xor eax,eax
}
我无法理解,为什么在析构函数存在的情况下,编译器无法内联函数T movz(T& t)
答案 0 :(得分:1)
是的,这是一个错误。 我已经在Qt上通过MinGW编译器环境测试了它。它很好地优化了一切。
首先,我已经更改了您的代码,如下所示,以便于查看汇编代码:
int main()
{
int i = foo();
std::cout << i << std::endl;
}
从我的Qt的调试反汇编:
45 int main()
46 {
0x401600 lea 0x4(%esp),%ecx
0x401604 <+0x0004> and $0xfffffff0,%esp
0x401607 <+0x0007> pushl -0x4(%ecx)
0x40160a <+0x000a> push %ebp
0x40160b <+0x000b> mov %esp,%ebp
0x40160d <+0x000d> push %ecx
0x40160e <+0x000e> sub $0x54,%esp
0x401611 <+0x0011> call 0x402160 <__main>
0x401616 <+0x0016> movl $0x5,-0x10(%ebp)
47 int i = foo();
0x401683 <+0x0083> mov %eax,-0xc(%ebp)
48 std::cout << i << std::endl;
0x401686 <+0x0086> mov -0xc(%ebp),%eax
0x401689 <+0x0089> mov %eax,(%esp)
0x40168c <+0x008c> mov $0x6fcba2c0,%ecx
0x401691 <+0x0091> call 0x401714 <_ZNSolsEi>
0x401696 <+0x0096> sub $0x4,%esp
0x401699 <+0x0099> movl $0x40171c,(%esp)
0x4016a0 <+0x00a0> mov %eax,%ecx
0x4016a2 <+0x00a2> call 0x401724 <_ZNSolsEPFRSoS_E>
0x4016a7 <+0x00a7> sub $0x4,%esp
49 }
0x4016aa <+0x00aa> mov $0x0,%eax
0x4016af <+0x00af> mov -0x4(%ebp),%ecx
0x4016b2 <+0x00b2> leave
0x4016b3 <+0x00b3> lea -0x4(%ecx),%esp
0x4016b6 <+0x00b6> ret
您甚至可以看到foo()已经过优化。您可以看到变量“i”直接分配给5并打印出来。