14

I know that the code written below is illegal

void doSomething(std::string *s){}
int main()
{
     doSomething(&std::string("Hello World"));
     return 0;
}

The reason is that we are not allowed to take the address of a temporary object. But my question is WHY?

Let us consider the following code

class empty{};
int main()
{
      empty x = empty(); //most compilers would elide the temporary
      return 0;
}

The accepted answer here mentions

"usually the compiler consider the temporary and the copy constructed as two objects that are located in the exact same location of memory and avoid the copy."

According to the statement it can be concluded that the temporary was present in some memory location( hence its address could have been taken) and the compiler decided to eliminate the temporary by creating an in-place object at the same location where the temporary was present.

Does this contradict the fact that the address of a temporary cannot be taken?

I would also like to know how is return value optimization implemented. Can someone provide a link or an article related to RVO implementation?

Community
  • 1
  • 1
Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345

7 Answers7

15
&std::string("Hello World")

The problem with this isn't that std::string("Hello World") yields a temporary object. The problem is that the expression std::string("Hello World") is an rvalue expression that refers to a temporary object.

You cannot take the address of an rvalue because not all rvalues have addresses (and not all rvalues are objects). Consider the following:

42

This is an integer literal, which is a primary expression and an rvalue. It is not an object, and it (likely) does not have an address. &42 is nonsensical.

Yes, an rvalue may refer to an object, as is the case in your first example. The problem is that not all rvalues refer to objects.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • @Rambo: A temporary object must have an address because an object, by definition, is a region of storage, and the storage occupied by an object must be addressable. The problem is that a temporary object is referred to by an rvalue expression but not all rvalue expressions refer to objects. `42` is not a temporary object; it is an integer literal. – James McNellis Nov 29 '10 at 06:09
  • 1
    The number `42` is an object and it has type `int`. It may be bound to a const reference, and if so its address may be taken. The compiler always tries to eliminate addresses which are not evaluated (that is, hoist values to registers), regardless of the object type being primitive or not. – Potatoswatter Nov 29 '10 at 06:21
  • 2
    @Potatoswatter: `42` is not an object, it is a value (and has type `int`). If it is bound to a const reference (i.e., `const int& x = 42;`) then a temporary object is constructed with the value `42` and the reference is bound to that temporary object. (FWIW, I subconsciously used the term "rvalue" instead of "temporary," forgetting that a temporary object to which a reference has been bound is still a temporary object; I'll have add a note about that...) – James McNellis Nov 29 '10 at 06:24
  • It is not an object unless it is used as such. But if it is used as an object, an object is created. `&42` is exactly as nonsensical as `& static_cast( 42 )`. Which is still somewhat nonsensical, but not utterly so. See my answer. – Potatoswatter Nov 29 '10 at 06:41
  • 1
    Not all rvalues refer to objects/have an address, but all rvalue *temporaries* have address/refer to objects. the name *temporary* classifies their lifetime, which is temporary. if there is no object, there is nothing to be temporary. – Johannes Schaub - litb Dec 05 '11 at 10:02
  • 42 is stored in memory or object code "somewhere", is it not? If it is, why would taking the address of 42 with &(42) be non-sensical? – Michael Gazonda Aug 22 '14 at 19:09
  • 1
    @Michael Gazonda Excuse the necromancy, but: on an actual computer, a lot of data may very well never appear in addressable RAM, but only in registers. Other data, such as integer literals, might additionally show up as a portion of a machine code operation, but it's not like C++ let's you take addresses to that (outside of function entry points) – hegel5000 Jul 16 '20 at 20:03
8

Long answer:

[...] it can be concluded that the temporary was present in some memory location

By definition:

  • "temporary" stands for: temporary object
  • an object occupies a region of storage
  • all objects have an address

So it doesn't take a very elaborate proof to show that a temporary has an address. This is by definition.

OTOH, you are not just fetching the address, you are using the builtin address-of operator. The specification of the builtin address-of operator says that you must have a lvalue:

  • &std::string() is ill-formed because std::string() is a rvalue. At runtime, this evaluation of this expression creates a temporary object as a side-effect, and the expression yield a rvalue that refers to the object created.
  • &(std::string() = "Hello World") is well-formed because std::string() = "Hello World" is a lvalue. By definition, a lvalue refers to an object. The object this lvalue refers to is the exact same temporary

Short answer:

This is the rule. It doesn't need the (incorrect, unsound) justifications some people are making up.

Community
  • 1
  • 1
curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • 1
    This shows that an Rvalue can be on the left side of an assignment. Nonsensical... but true!!! – GetFree Apr 15 '17 at 06:56
4

$5.3.1/2 - "The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualifiedid."

Expressions such as

99

A() // where A is a user defined class with an accessible 
    // and unambiguous default constructor

are all Rvalues.

$3.10/2 - "An lvalue refers to an object or function. Some rvalue expressions—those of class or cv-qualified class type—also refer to objects.47)"

And this is my guess: Even though Rvalues may occupy storage (e.g in case of objects), C++ standard does not allow taking their address to maintain uniformity with the built-in types

Here's something interesting though:

void f(const double &dbl){
   cout << &dbl;
}

int main(){
   f(42);
}

The expression '42' is an Rvalue which is bound to the 'reference to const double' and hence it creates a temporary object of type double. The address of this temporary can be taken inside the function 'f'. But note that inside 'f' this is not really a temporary or a Rvalue. The moment it is given a name such as 'dbl', it is treated as an Lvalue expression inside 'f'.

Here's something on NRVO (similar)

Chubsdad
  • 24,777
  • 4
  • 73
  • 129
  • "_But note that inside 'f' this is not really a temporary_" Of course it is: the reference is bound to the temporary object (an unnamed object) created by `f(42)`. You can say that `dbl` "names" the object, inside `f`, but the object still exists (just) after the function returns, and it has no name at this point. – curiousguy Dec 05 '11 at 09:28
3

A temporary is an example of a C++ "rvalue." It is supposed to purely represent a value within its type. For example, if you write 42 in two different places in your program, the instances of 42 are indistinguishable despite probably being in different locations at different times. The reason you can't take the address is that you need to do something to specify that there should be an address, because otherwise the concept of an address is semantically unclean and unintuitive.

The language requirement that you "do something" is somewhat arbitrary, but it makes C++ programs cleaner. It would suck if people made a habit of taking addresses of temporaries. The notion of an address is intimately bound with the notion of a lifetime, so it makes sense to make "instantaneous" values lack addresses. Still, if you are careful, you can acquire an address and use it within the lifetime that the standard does allow.

There are some fallacies in the other answers here:

  • "You cannot take the address of an rvalue because not all rvalues have addresses." — Not all lvalues have addresses either. A typical local variable of type int which participates in a simple loop and is subsequently unused will likely be assigned a register but no stack location. No memory location means no address. The compiler will assign it a memory location if you take its address, though. The same is true of rvalues, which may be bound to const references. The "address of 42" may be acquired as such:

    int const *fortytwo_p = & static_cast<int const &>( 42 );
    

    Of course, the address is invalid after the ; because temporaries are temporary, and this is likely to generate extra instructions as the machine may pointlessly store 42 onto the stack.

    It's worth mentioning that C++0x cleans up the concepts by defining the prvalue to be the value of the expression, independent of storage, and the glvalue to be the storage location independent of its contents. This was probably the intent of the C++03 standard in the first place.

  • "Then you could modify the temporary, which is pointless." — Actually temporaries with side effects are useful to modify. Consider this:

    if ( istringstream( "42" ) >> my_int )
    

    This is a nice idiom for converting a number and checking that the conversion succeeded. It involves creating a temporary, calling a mutating function on it, and then destroying it. Far from pointless.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • 2
    I disagree with the first point. Yes, if you don't take the address of an object a compiler may perform optimizations such that it no longer has an address. This is irrelevant though, because every lvalue refers to an object, so it is always possible to take the address of an lvalue (an lvalue may also refer to a function, but I don't think that's important to this). The same is not the case for rvalues: it isn't possible to take the address of the rvalue `42` because it is not an object. (`static_cast( 42 )` creates a temporary object with the value `42`; `42` is not an object) – James McNellis Nov 29 '10 at 06:47
  • @James: The compiler is allowed to create a temporary for any random purpose. Where in the standard does it say a temporary is initialized with the rvalue `42`, which was not a temporary beforehand? — Also, this is a bit off topic, since OP was asking about a `std::string` temporary, which is certainly an object anyway. – Potatoswatter Nov 29 '10 at 06:53
  • 1
    3.10/2 states that `42` is not an object. 8.5.3/5 (second to last bullet, the one that ends with footnote 93) states that a temporary object is created for the reference initialization. The reason that this is on topic is thus: you can only take the address of an object. All lvalues refer to objects, therefore you can apply `&` to all lvalues. The same is not true for rvalues: some rvalue expressions do not refer to objects, therefore you cannot apply `& to all rvalues. `42` is just an example of an rvalue that does not refer to an object. – James McNellis Nov 29 '10 at 07:06
  • @James: 3.10/2 says that `std::string( "hello" )` is an object, but it doesn't positively say that `42` is not. I do believe you're right from a language semantic point of view, anyway. As for applying `&`, it simply requires an lvalue; rvalues with underlying objects are not allowed. That breaks the tautology. In theory and practice, the address pops into existence if and when you want it — which means, one way or another, acquiring an lvalue from an expression. That you don't begin with an lvalue doesn't mean you can't have one in the end. – Potatoswatter Nov 29 '10 at 07:27
  • "_A temporary is an example of a C++ "rvalue."_" This statement does not make sense: "temporary" stands for temporary **object**; "rvalue" stands for rvalue **expression**. Expressions exist at compile-time, and objects exists at run-time. – curiousguy Dec 05 '11 at 08:08
  • 1
    @curious in the spec, temporary xan be an attribute of an expression aswell as an attribute of an pbject. an expression is a temporary if it directly refees to a temporary object. that is very similar to the notion of a "bitfield lvalue". – Johannes Schaub - litb Dec 05 '11 at 10:11
2

It can be taken, but once the temporary ceases to exist, you have a dangling pointer left.

EDIT

For the downvoters:

const std::string &s = std::string("h");
&s;

is legal. s is a reference to a temporary. Hence, a temporary object's address can be taken.

EDIT2

Bound references are aliases to what they are bound to. Hence, a reference to a temporary is another name for that temporary. Hence, the second statement in the paragraph above holds.

OP's question is about temporaries (in terms of the words he uses), and his example is about rvalues. These are two distinct concepts.

lijie
  • 4,811
  • 22
  • 26
  • 4
    The operand of the built-in address-of operator (the unary `&`) must be an lvalue. – James McNellis Nov 29 '10 at 06:01
  • 1
    the operand of the address-of operator must be an lvalue, but a temporary can have its address taken, because expressions are/are not l/rvalues, not objects. – lijie Nov 29 '10 at 06:10
  • Expressions are not lvalues and cannot have their addresses taken. Your example is irrelevant as s isn't a temporary. – user207421 Nov 29 '10 at 06:17
  • You make a good point that a temporary object to which a const reference has been bound can be referred to via an lvalue expression. I don't think that's what the OP means, but it's certainly a valid point. – James McNellis Nov 29 '10 at 06:21
  • 2
    @EJP: `s` is not a temporary, no. `s` is a reference that is bound to a temporary object, so `s` denotes a temporary object. – James McNellis Nov 29 '10 at 06:22
  • @James : I have read [here](http://stackoverflow.com/questions/2145030/are-all-temporaries-rvalues-in-c/2145824#2145824) that temporaries however can be bound to const references and doing so increases the lifetime of the temporary. However since `s` is a reference and all references are lvalues as AndreyT says in his post we can take its address. – Prasoon Saurav Nov 29 '10 at 06:27
  • @EJP: `s` is a reference means it is an alias means once it is bound, it is as good as the temporary which it is bound to. – lijie Nov 29 '10 at 06:44
  • @James: yes, I don't know whether the question is a misnomer; he ascribes the illegality of the code to not being able to take the address of a temporary, which isn't true. well, the whole question sorta assumes that temporary is a synonym for rvalue. – lijie Nov 29 '10 at 06:46
  • @lijie : Also read my this question http://stackoverflow.com/questions/2145030/are-all-temporaries-rvalues-in-c/2145824#2145824 – Prasoon Saurav Nov 29 '10 at 06:50
  • Again, a perfectly correct answer is downvoted. This is stackoverflow. – curiousguy Dec 03 '11 at 12:16
0

One reason is that your example would give the method write access to the temporary, which is pointless.

The citation you provided isn't about this situation, it is a specific optimization that is permitted in declarators with initializers.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • @downvoter please explain. All the other correct answers say much the same thing albeit in more technical language. – user207421 Dec 05 '11 at 09:07
  • `std::string()=std::string("Hello World")` – curiousguy Dec 05 '11 at 13:50
  • @curiousguy Very nice, what is it? Looks like it creates an anonymous object which holds a copy of the temporary on the RHS. – user207421 Dec 05 '11 at 23:42
  • 1
    It creates a temporary object, then assigns it the value of another temporary object. It is legal code showing that C++ does not forbid writing to a temporary object. – curiousguy Dec 06 '11 at 13:30
  • @curiousguy It is legal code showing that C++ does not forbid *initializing* a temporary object. Not the same thing. Temporary objects wouldn't be much use if they couldn't be initialized. – user207421 Dec 06 '11 at 23:43
  • Hug? What do you think `std::string()=std::string("Hello World");` means? – curiousguy Dec 07 '11 at 00:21
  • @curiousguy It declares a temporary object with an initial value. It doesn't 'create it and then assign it'. Nor does it take the address of a temporary object, which is what this thread is actually about; nor does it give you a way to change the value of the temporary object either, which is what I meant by 'write access'. It is in fact an instance of the special optimization for initializers I referred to in my answer. – user207421 Dec 07 '11 at 00:54
  • 1
    "_It doesn't 'create it and then assign it'._" Of course it does. `x = y` is assignment in C++. – curiousguy Dec 07 '11 at 02:16
  • @curiousguy I will say it again. That is a declaration with initialization. It is not the same thing as an assignment. It doesn't call operator= for example. – user207421 Dec 07 '11 at 02:38
  • 1
    "_That is a declaration with initialization._" I will say it again: this is assignment. "_That is a declaration_" Hug??? it declares what? You apparently don't know C++. – curiousguy Dec 07 '11 at 03:05
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/5641/discussion-between-curiousguy-and-ejp) – curiousguy Dec 07 '11 at 03:17
  • @curiousguy Not until you have had a look at the C++ standard and understood the difference between declaration+initialization and assignment. – user207421 Dec 07 '11 at 09:40
  • EJP, have **you** finally looked at a C++ grammar? – curiousguy Dec 08 '11 at 14:12
  • @curiousguy Yes, I have been looking at C++ grammars for 20 years. I refer you to ISO/IEC 14882 #8.5 'Initializers'. If initialization and assignment were the same thing in C++ this section would not exist. – user207421 Dec 13 '11 at 09:41
  • @EJP: It's not a declaration because it has no *declarator-id*, and the *init-declarator-list* can be omitted only when declaring a class or enumeration. – Ben Voigt Nov 16 '16 at 16:06
-1

Why is taking the address of a temporary illegal?

The scope of temporary variables are limited to some particular method or some block, as soon as the method call returns the temporary variables are removed from the memory, so if we return the address of a variable which no longer exists in the memory it does not make sense. Still the address is valid but that address may now contain some garbage value.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Vishrant
  • 15,456
  • 11
  • 71
  • 120