0

I got the snippet below from 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;
}

with the following disassembly obtained from 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  
}

which shows (see the characters "<<======" introduced by me in the disassembly) that the compiler didn't evaluate the function POW at compile time. From his comment, @CaffeineAddict seemed to expect this behavior from the compiler. But I still can't understand why was this expected at all?

Community
  • 1
  • 1
John Kalane
  • 1,163
  • 8
  • 17
  • 1
    What is it **you** were expecting? You never asked the compiler to evaluate it at compile-time. (your title seems to contradict the body of the question...) – Marc Glisse Apr 11 '16 at 13:07
  • @MarcGlisse The function `POW` is `constexpr` and it's being called with constant expressions. I've just edited the title. Thanks for calling my attention. – John Kalane Apr 11 '16 at 13:12
  • Looking at the debug build code gen is never very useful. Use const auto value = POW(2, 63); to force the compiler to do what you want it to do and guarantee a happy outcome. Get insight why this voids the warranty by passing (2, -1) instead :) – Hans Passant Apr 11 '16 at 13:30
  • @HansPassant `Get insight why this voids the warranty by passing (2, -1) instead` What does that have to do with calling the function with (2, 63)? The snippet executed normally without any exception. – John Kalane Apr 11 '16 at 13:42

1 Answers1

0

Two reasons.

First, a constexpr function isn't guaranteed to be called at compile time. To force the compiler to call it at compile time, you must store it in a constexpr variable first, i.e.

constexpr auto pow = POW((unsigned __int64)2, 63);
std::cout << pow << std::endl;

Secondly, you must build the project in Release configuration. In VS, you'll find you can break-point through constexpr functions if you have built the project using Debug configuration.

Deus Sum
  • 146
  • 2
  • It seems like the VS2015 compiler evaluates a constexpr function at compile time **only** in those situations where a constant expression is required, as in your example above, irrespective whether it's a debug or a release build. – John Kalane Apr 11 '16 at 15:05
  • Are you sure about that? Here's an image of the disassembly, even using static_assert to make sure there's no mistake about it being compile time: http://imgur.com/ElPkyx3 – Deus Sum Apr 12 '16 at 05:40
  • @DeusSum You didn't use VS2015 in the linked example, nor clang or GCC. The three compilers evaluate the function `POW` at compile time, and that's exactly what they are supposed to do, according to [dcl.constexpr]/9 in the C++14 Standard. See also [this](http://eel.is/c++draft/dcl.constexpr#9). Then, I must conclude that the compiler you're using has a bug, according to alluded paragraph. – Belloc Apr 13 '16 at 18:10
  • 1
    "*you must store it in a constexpr variable first, i.e. *" Where are you storing anything in a constexpr variable? – ildjarn Apr 15 '16 at 00:41
  • I'm not sure how I didn't notice that earlier, but it is fixed now. Thanks. – Deus Sum Apr 15 '16 at 03:52