Using g++ built with default optimization flags:
float f = rand(); 40117e: e8 75 01 00 00 call 4012f8 <_rand> 401183: 89 44 24 1c mov %eax,0x1c(%esp) 401187: db 44 24 1c fildl 0x1c(%esp) 40118b: d9 5c 24 2c fstps 0x2c(%esp) std::cout << sin(f) << " " << sin(f); 40118f: d9 44 24 2c flds 0x2c(%esp) 401193: dd 1c 24 fstpl (%esp) 401196: e8 65 01 00 00 call 401300 <_sin> <----- 1st call 40119b: dd 5c 24 10 fstpl 0x10(%esp) 40119f: d9 44 24 2c flds 0x2c(%esp) 4011a3: dd 1c 24 fstpl (%esp) 4011a6: e8 55 01 00 00 call 401300 <_sin> <----- 2nd call 4011ab: dd 5c 24 04 fstpl 0x4(%esp) 4011af: c7 04 24 e8 60 40 00 movl $0x4060e8,(%esp)
Built with -O2:
float f = rand(); 4011af: e8 24 01 00 00 call 4012d8 <_rand> 4011b4: 89 44 24 1c mov %eax,0x1c(%esp) 4011b8: db 44 24 1c fildl 0x1c(%esp) std::cout << sin(f) << " " << sin(f); 4011bc: dd 1c 24 fstpl (%esp) 4011bf: e8 1c 01 00 00 call 4012e0 <_sin> <----- 1 call
From this we can see that without optimizations the compiler uses 2 calls and just 1 with optimizations, empirically I guess, we can say the compiler does optimize the call.
sinis defined in the same compilation unit, the compiler does not know howsinis implemented, so at best this would happen at link time.std::ostream::operator<<didn't change the value off.