编译器决定调用函数 `POW`,而不是在编译时对其求值。为什么?
The compiler decided to call the function `POW`, instead of evaluating it at compile time. Why?
我从 this comment by @CaffeineAddict 那里得到了下面的片段。
#include <iostream>
template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
return (expo != 0) ? base * POW(base, expo - 1) : 1;
}
int main(int argc, char** argv)
{
std::cout << POW((unsigned __int64)2, 63) << std::endl;
return 0;
}
使用从 VS2015 获得的以下反汇编:
int main(int argc, char** argv)
{
009418A0 push ebp
009418A1 mov ebp,esp
009418A3 sub esp,0C0h
009418A9 push ebx
009418AA push esi
009418AB push edi
009418AC lea edi,[ebp-0C0h]
009418B2 mov ecx,30h
009418B7 mov eax,0CCCCCCCCh
009418BC rep stos dword ptr es:[edi]
std::cout << POW((unsigned __int64)2, 63) << std::endl;
009418BE mov esi,esp
009418C0 push offset std::endl<char,std::char_traits<char> > (0941064h)
009418C5 push 3Fh
009418C7 push 0
009418C9 push 2
009418CB call POW<unsigned __int64,int> (09410FAh) <<========
009418D0 add esp,0Ch
009418D3 mov edi,esp
009418D5 push edx
009418D6 push eax
009418D7 mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (094A098h)]
009418DD call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (094A0ACh)]
009418E3 cmp edi,esp
009418E5 call __RTC_CheckEsp (0941127h)
009418EA mov ecx,eax
009418EC call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (094A0B0h)]
009418F2 cmp esi,esp
009418F4 call __RTC_CheckEsp (0941127h)
return 0;
009418F9 xor eax,eax
}
这表明(参见我在反汇编中引入的字符“<<======”)编译器在编译时没有计算函数 POW
。从他的评论来看,@CaffeineAddict 似乎期望编译器会出现这种行为。但我还是不明白为什么会出现这种情况?
两个原因。
首先,不能保证在编译时调用 constexpr 函数。要强制编译器在编译时调用它,您必须先将它存储在一个 constexpr 变量中,即
constexpr auto pow = POW((unsigned __int64)2, 63);
std::cout << pow << std::endl;
其次,您必须在发布配置中构建项目。在 VS 中,如果您使用调试配置构建项目,您会发现可以通过 constexpr 函数断点。
我从 this comment by @CaffeineAddict 那里得到了下面的片段。
#include <iostream>
template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
return (expo != 0) ? base * POW(base, expo - 1) : 1;
}
int main(int argc, char** argv)
{
std::cout << POW((unsigned __int64)2, 63) << std::endl;
return 0;
}
使用从 VS2015 获得的以下反汇编:
int main(int argc, char** argv)
{
009418A0 push ebp
009418A1 mov ebp,esp
009418A3 sub esp,0C0h
009418A9 push ebx
009418AA push esi
009418AB push edi
009418AC lea edi,[ebp-0C0h]
009418B2 mov ecx,30h
009418B7 mov eax,0CCCCCCCCh
009418BC rep stos dword ptr es:[edi]
std::cout << POW((unsigned __int64)2, 63) << std::endl;
009418BE mov esi,esp
009418C0 push offset std::endl<char,std::char_traits<char> > (0941064h)
009418C5 push 3Fh
009418C7 push 0
009418C9 push 2
009418CB call POW<unsigned __int64,int> (09410FAh) <<========
009418D0 add esp,0Ch
009418D3 mov edi,esp
009418D5 push edx
009418D6 push eax
009418D7 mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (094A098h)]
009418DD call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (094A0ACh)]
009418E3 cmp edi,esp
009418E5 call __RTC_CheckEsp (0941127h)
009418EA mov ecx,eax
009418EC call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (094A0B0h)]
009418F2 cmp esi,esp
009418F4 call __RTC_CheckEsp (0941127h)
return 0;
009418F9 xor eax,eax
}
这表明(参见我在反汇编中引入的字符“<<======”)编译器在编译时没有计算函数 POW
。从他的评论来看,@CaffeineAddict 似乎期望编译器会出现这种行为。但我还是不明白为什么会出现这种情况?
两个原因。
首先,不能保证在编译时调用 constexpr 函数。要强制编译器在编译时调用它,您必须先将它存储在一个 constexpr 变量中,即
constexpr auto pow = POW((unsigned __int64)2, 63);
std::cout << pow << std::endl;
其次,您必须在发布配置中构建项目。在 VS 中,如果您使用调试配置构建项目,您会发现可以通过 constexpr 函数断点。