34

§5.3.1 Unary operators, Section 3

The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id.

What exactly does "shall be" mean in this context? Does it mean it's an error to take the address of a temporary? I was just wondering, because g++ only gives me a warning, whereas comeau refuses to compile the following program:

#include <string>

int main()
{
    &std::string("test");
}

g++ warning: taking address of temporary

comeau error: expression must be an lvalue or a function designator

Does anyone have a Microsoft compiler or other compilers and can test this program, please?

Dharman
  • 30,962
  • 25
  • 85
  • 135
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • that's interesting. I don't have any experience with comeau, but I wonder if it is getting confused because of the template? What happens if you just try something like &array[0]; where array is some created array? – Casey Feb 17 '10 at 12:58
  • &Casey &array[0] is perfectly legal - array[0] is an lvalue –  Feb 17 '10 at 13:00

7 Answers7

37

The word "shall" in the standard language means a strict requirement. So, yes, your code is ill-formed (it is an error) because it attempts to apply address-of operator to a non-lvalue.

However, the problem here is not an attempt of taking address of a temporary. The problem is, again, taking address of a non-lvalue. Temporary object can be lvalue or non-lvalue depending on the expression that produces that temporary or provides access to that temporary. In your case you have std::string("test") - a functional style cast to a non-reference type, which by definition produces a non-lvalue. Hence the error.

If you wished to take address of a temporary object, you could have worked around the restriction by doing this, for example

const std::string &r = std::string("test");
&r; // this expression produces address of a temporary

whith the resultant pointer remaining valid as long as the temporary exists. There are other ways to legally obtain address of a temporary object. It is just that your specific method happens to be illegal.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 8
    +1 for difference of rvalue and temporary. People make them equal often, but they aren't at all. A common example are exception objects, which are lvalues and can be bound to a `T&` in a catch clause. – Johannes Schaub - litb Feb 19 '10 at 13:26
  • 1
    In fact, the Standard itself seems to misuse the term "temporary", if i'm not mistaken. It says that a temporary bound to a function return should be destroyed after the function returns. However, this means that `struct A { A &ref() { i++; return *this; } int i; }; int main() { A().ref().ref(); }` invokes undefined behavior, because after the first `ref()`, the object doesn't exist anymore. It misses to say that only such objects are considered, that haven't yet been bound to a reference (including the implicit object parameter). – Johannes Schaub - litb Feb 19 '10 at 13:46
  • @JohannesSchaub-litb "_It says that a temporary bound to a function return should be destroyed after the function returns._" It seems that temporary and rvalue are used as if they were synonyms. – curiousguy Dec 25 '11 at 22:51
  • @JohannesSchaub-litb "_A common example are exception objects_" Are you sure an exception object is a temporary? It sounds wrong. – curiousguy Dec 25 '11 at 22:53
  • @curiousguy: An exception object is always a temporary, i.e. whenever you throw something, it is actually a temporary copy of that "something" that gets thrown. This makes perfect sense to me, since the exception object's lifetime is managed implicitly behind the scenes. The compiler has to *own* it to have the necessary freedom to manage it. So, it makes its own copy. Of course, you can throw pointers to objects instead of actual objects, in which case you will retain ownership of the object. But I'd say that this is not a good practice. – AnT stands with Russia Dec 26 '11 at 06:32
  • @AndreyT "_a temporary copy of that "something" that gets thrown._" Yes, `throw` makes a temporary copy. But @JohannesSchaub-litb was describing reference binding "_in a catch clause_": the `catch` declaration itself doesn't "manage the exception object's lifetime" (but a `goto` outside of the catch block does). The `catch` declaration does not deal with a temporary (aka "rvalue") it manages, but with a lvalue: mostly, the exception object is handled by a (non-shared) `shared_ptr`, this `shared_ptr` is a temporary for the catch block, the `shared_ptr` is not. (Of course, it is more than that.) – curiousguy Dec 26 '11 at 07:55
  • @curious yes. the exception object that is allocated in an unspecified manner is a temporary object. For the purpose of defining how copy initialization to a catch clause declaration behaves, it is considered to be an lvalue (which the spec says explicitly). Conceptually, the operand of "throw" initializes the object or reference declared in a catch clause. That there is an object behind the scenes that stores the value in between is transparent to the programmer. Hence, I think, the property "temporary". – Johannes Schaub - litb Dec 26 '11 at 19:15
  • @JohannesSchaub-litb I believe "temporary" implies "type is known". – curiousguy Feb 18 '12 at 05:25
  • @Nestor: I'm not sure what point you are trying to make about `r`. `r` itself is not an object at all - it is a reference, an entity of non-object type. But it doesn't matter what `r` itself is since `&` is not applied to `r` itself but rather to the result of expression `r`. It is a reference that refers to the same temporary object created by `std::string("test")`. And no, `r` is not an xvalue. A *named* reference is always an lvalue. – AnT stands with Russia Aug 19 '19 at 04:10
6

When the word "shall" is used in the C++ Standard, it means "must on pain of death" - if an implementation does not obey this, it is faulty.

  • 1
    -1. Actually, this applies only to those statements of the form "the implementation shall do X". Statements of the form "the input code shall do X", it means "must on pain of diagnostic". – MSalters Feb 17 '10 at 15:21
  • 2
    @MSalters Must on pain of death issue a diagnostic. –  Feb 17 '10 at 15:24
  • 3
    @MSalters And BTW, I've always thought it was dubious practice to downvote answers to a question you yourself are supplying an answer to. –  Feb 17 '10 at 15:32
  • 8
    I don't see why. If you think an answer is incorrect, it should be downvoted whether or not you've written an answer on your own. I think we're mature enough to handle that, aren't we? Or do people really take the rep-race that seriously? – jalf Feb 17 '10 at 15:41
  • 1
    Look, the point of SO is to act as a repository of answers. There are usually multiple answers, so how do you (as a later reader) pick the sensible ones? Voting is intended to highlight the good ones. I answered _explicitly_ because I found the highest-voted one incorrect, and for that very same reason I downvoted it. – MSalters Feb 17 '10 at 15:45
  • @jalf I think it would be politer to point out the error (which MSalters has not done - my answer is perfectly correct) and wait for a correction before downvoting. That's what I try to do, anyway. –  Feb 17 '10 at 15:47
  • Consider the "pain of death" statement again. If the requirement is on input code, your interpretation reads as a pain of death _for the code_, ie. the code _MUST NOT_ compile. This is factually untrue. The code may very well compile, with the only pain being a diagnostic. – MSalters Feb 17 '10 at 15:50
  • @MSalters I nowhere said and did not mean that the code "MUST NOT compile". Please don't put words into my mouth. –  Feb 17 '10 at 16:09
  • That's the entire point: you wrote "must on pain of death" - what does that mean from the viewpoint of a _coder_ (not a compiler vendor)? The relevant quote states an obligation on the coder (put an lvalue after &), so the consequences ("pain of death") would logically be a consequence for the code. – MSalters Feb 17 '10 at 16:19
  • @MSalters To quote from your own answer: "So, in this particular case, a conformant compiler must give a diagnostic" - how does this contradict what I wrote? –  Feb 17 '10 at 16:22
  • In fact, it says the argument could be a `qualified-id`. This wrongly includes some rvalue cases, i think: `struct A { enum { HAHA }; }; int main() { &A::HAHA; }`. It's a nonstatic member accessed using a qualified-id, so the standard gives it type "pointer to member of class A of type ". I think it should say "nonstatic data member or nonstatic member function", and say at the end "otherwise, the program is ill-formed.". Not sure tho... – Johannes Schaub - litb Feb 19 '10 at 13:24
3

It is permitted in MSVC with the deprecated /Ze (extensions enabled) option. It was allowed in previous versions of MSVC. It generates a diagnostic with all warnings enabled:

warning C4238: nonstandard extension used : class rvalue used as lvalue.

Unless the /Za option is used (enforce ANSI compatibility), then:

error C2102: '&' requires l-value

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

&std::string("test"); is asking for the address of the return value of the function call (we'll ignore as irrelevant the fact that this function is a ctor). It didn't have an address until you assign it to something. Hence it's an error.

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 1
    You can of course take the address of function call return values, provided they are lvalues. –  Feb 17 '10 at 13:08
  • But if you're calling a function, isn't that by definition an rvalue? – TMN Feb 17 '10 at 13:21
  • 2
    @TMN No - in C++ you can return a reference, which is an lvalue. That's how things like operator[] for std::vector work. –  Feb 17 '10 at 13:28
  • 4
    @James Curran: Incorrect. This is not a function call. This is a *functional-style cast*. And you can take the adress of the result of a cast (as well as of the result of a function call) if it is an *lvalue*. A reference, for example, is always an lvalue. – AnT stands with Russia Feb 17 '10 at 15:35
1

The C++ standard is a actually a requirement on conformant C++ implementations. At places it is written to distinguish between code that conformant implementations must accept and code for which conformant implementations must give a diagnostic.

So, in this particular case, a conformant compiler must give a diagnostic if the address of an rvalue is taken. Both compilers do, so they are conformant in this respect.

The standard does not forbid the generation of an executable if a certain input causes a diagnostic, i.e. warnings are valid diagnostics.

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

I'm not a standards expert, but it certainly sounds like an error to me. g++ very often only gives a warning for things that are really errors.

Hans W
  • 3,851
  • 1
  • 22
  • 21
-3

user defined conversion

struct String {
    std::string str;

    operator std::string*() {
        return &str;
    }
};

std::string *my_str = String{"abc"};
SPapiernik
  • 7
  • 1
  • 1