Визуальное понимание функции C ++

Я использую ключевое слово, специфичное для 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)

  • Заметка Поведение соответствует с 2008 по 2013
  • Заметка Я проверил с помощью cygwin-gcc, но компилятор встроил код. В данный момент я не могу проверить другие компиляторы, но при необходимости обновлю их в течение следующих 12 часов.

9

Решение

Да, это ошибка.
Я проверил его в среде компиляции 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 и напечатана.

1

Другие решения


По вопросам рекламы [email protected]