c++order-of-execution

Order of execution is different in different compilers


This code gives different outputs in XCode and in Visual Studio:

#include <iostream>

using namespace std;

int f() {
    cout << 'A';
    return 1;
}

int main() {
    cout << '.' << f();
    return 0;
}

In Visual Studio it outputs

A.1

In XCode it outputs

.A1

Obviously I'd expect both compilers to output the same thing.. Is it to be expected to not do that? Is it a known thing or is there anything I could do about this?


Solution

  • MSVC compiles the code differently than GCC apparently.

    Inspecting the assembly code through Godbolt (using x86-64 gcc 13.2 and x64 msvc v19.38) shows that MSVC produces the following assembly for the main function:

    tv69 = 32
    main    PROC
    $LN3:
            sub     rsp, 56                             ; 00000038H
            call    int f(void)                         ; f
            mov     DWORD PTR tv69[rsp], eax
            mov     dl, 46                                    ; 0000002eH
            lea     rcx, OFFSET FLAT:std::basic_ostream<char,std::char_traits<char> > std::cout ; std::cout
            call    std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char) ; std::operator<<<std::char_traits<char> >
            mov     ecx, DWORD PTR tv69[rsp]
            mov     edx, ecx
            mov     rcx, rax
            call    std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(int) ; std::basic_ostream<char,std::char_traits<char> >::operator<<
            xor     eax, eax
            add     rsp, 56                             ; 00000038H
            ret     0
    main    ENDP
    

    ... while GCC produces this:

    main:
            push    rbp
            mov     rbp, rsp
            push    rbx
            sub     rsp, 8
            mov     esi, 46
            mov     edi, OFFSET FLAT:_ZSt4cout
            call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
            mov     rbx, rax
            call    f()
            mov     esi, eax
            mov     rdi, rbx
            call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
            mov     eax, 0
            mov     rbx, QWORD PTR [rbp-8]
            leave
            ret
    

    In the MSVC output, f() is called before the first operator<<, while GCC calls f() after calling the first operator<< on std::cout.

    The reason for this is that in C++, the order of evaluation is unspecified:

    Order of evaluation of any part of any expression, including order of evaluation of function arguments is unspecified (with some exceptions listed below). The compiler can evaluate operands and other subexpressions in any order, and may choose another order when the same expression is evaluated again.

    There is no concept of left-to-right or right-to-left evaluation in C++. This is not to be confused with left-to-right and right-to-left associativity of operators: the expression a() + b() + c() is parsed as (a() + b()) + c() due to left-to-right associativity of operator+, but c() may be evaluated first, last, or between a() or b() at run time.