Я использую ключевое слово, специфичное для MS, чтобы принудительно встроить глобальную функцию, но я заметил, что функция не может встроить себя, если она использует объект, который имеет явный тривиальный деструктор.
Цитировать из MSDN
Даже с
__forceinline
, компилятор не может встроенный код во всех
обстоятельства. Компилятор не может встроить функцию, если:
Функция или ее вызывающая сторона компилируются с
/Ob0
(опция по умолчанию для отладочных сборок).Функция и вызывающая сторона используют различные типы обработки исключений (обработка исключений в C ++ в одной, структурированная обработка исключений).
в другом).Функция имеет переменный список аргументов.
Функция использует встроенную сборку, если не скомпилирована с
/Og
,/Ox
,/O1
, или же/O2
,Функция рекурсивная и не сопровождается
#pragma inline_recursion(on)
, С прагмой рекурсивные функции встроены до глубины по умолчанию 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)
Да, это ошибка.
Я проверил его в среде компиляции 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 и напечатана.