C++ is specified in terms of operations against a theoretical model of computer memory.
It also has a feature known as the "as if" rule. That means that the compiler can generate any code it likes, provided that the overall observable effect is "as if" the code you have written was literally translated to operations against the memory model.
In unoptimised code, the assembler produced is in reality very close to the operations expressed in code, for example gcc might produce the following code for your function:
a(int): # @a(int)
push rbp
mov rbp, rsp
mov dword ptr [rbp - 4], edi
mov edi, dword ptr [rbp - 4]
shl edi, 1
mov dword ptr [rbp - 8], edi
mov eax, dword ptr [rbp - 8]
pop rbp
ret
and given the following calling code:
extern void foo(int x);
int main()
{
foo(a(2));
}
The following code might be produced:
main: # @main
push rbp
mov rbp, rsp
mov edi, 2
call a(int)
mov edi, eax
call foo(int)
xor eax, eax
pop rbp
ret
In this simple program, the observable effect of the code is that foo will be called with an argument of value 4. The call to a only has one observable side-effect. That is, it's return value is double its input value.
Because the returned value is passed directly into foo and not stored anywhere, we could say that all side effects of calling a are completely consumed by a call to foo.
Therefore, if a compiler knows what a does, it doesn't need to generate code to call it. It can just call foo with the value derived 'as if' calling a(2).
Indeed, adding optimisation gives us this:
main: # @main
push rax
mov edi, 4 # note: 'as if' a(2)
call foo(int)
xor eax, eax
pop rcx
ret
The implementation of a in this case (on gcc) is as follows:
a(int): # @a(int)
# 'as if' we created a variable and did some arithmetic,
# stored the result and then returned the result
lea eax, [rdi + rdi]
ret