简体   繁体   中英

C++ reference to const with literal initialization

I'm diving a bit deeper into C++ and struggling with a reference to const which is initialized with an literal. eg

const int &r {100};

It definitly works but I'm wondering what the compiler makes with this kind of definition. Is there a real object created in the memory holding the value 100? Or is each occurance of r simply replaced by 100 in the code during compilation? This would be my guess because the initializer value couldn't either be changed nor referenced anyway during runtime, so why keep it in the memory?

Here:

const int &r {100};

a temporary int is being created and later is bound to r . Reference to const will prolong lifetime of temporary which is bound to it.

This is more usefull in cases like:

void foo (const std::string& s) {}    
foo("test"); // here temporary std::string is created and later on bound to `s`

In theory (the "abstract machine"), the literal is created as a temporary, and then the reference is bound to it, which extends it's life.

In practice, the compiler is allowed to change this however it wants so long as it produces the same "observable behaviour". So the compiler doesn't have hold the variable in memory.

You can see this by comparing the output of, for instance, g++ -O0 -g vs g++ -O3 -g and disassembling the code.

The short program:

int main() {
    const int &r {100};
    return r;
}

on my computer compiles with no optimization to:

0x00000000004005a8 <+0>: push   %rbp 
0x00000000004005a9 <+1>: mov    %rsp,%rbp 
0x00000000004005ac <+4>: mov    $0x64,%eax 
0x00000000004005b1 <+9>: mov    %eax,-0xc(%rbp) 
0x00000000004005b4 <+12>:    lea    -0xc(%rbp),%rax 
0x00000000004005b8 <+16>:    mov    %rax,-0x8(%rbp) 
0x00000000004005bc <+20>:    mov    -0x8(%rbp),%rax 
0x00000000004005c0 <+24>:    mov    (%rax),%eax 
0x00000000004005c2 <+26>:    pop    %rbp 
0x00000000004005c3 <+27>:    retq    

versus full optimization, where there is clearly no reference in memory (ie r is just replaced by a literal 100):

0x00000000004005b0 <+0>:    mov    $0x64,%eax
0x00000000004005b5 <+5>:    retq   

Formally, this generates an object which is then bound to the reference, and lives as long as the reference does.

There are situations where the object must be provided. Consider the situation where you have declared two functions, defined in a separate translation unit, like this:

void foo(int const*);
bool bar(int const*);

And now you do the following:

int main()
{
  const int& r{100};
  foo(&r);
  return bar(&r);
}

Not only has the compiler to pass a pointer to an object with the right value here, it has to pass a pointer to one and the same object, as it doesn't know the implementation in the other translation unit (that possibly hasn't even been written yet), which could be for example:

namespace
{
  int const* ptr;
}

void foo(int const* p)
{
  ptr = p;
}

bool bar(int const* p)
{
  return p == ptr && *p = 100;
}

If in that case, the compiler does anything else but create an actual int object with value 100, bound to r , the result will be incorrect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM