Pregunta

In a moment of haste, needing a pointer to an object to pass to a function. I took the address of an unnamed temporary object and to my surprise it compiled (the original code had warnings turned further down and lacked the const correctness present in the example below). Curious, I set up a controlled environment with warnings all the way up and treating warnings as errors in Visual Studio 2013.

Consider the following code:

class Contrived {
  int something;
};

int main() {
  const Contrived &r  = Contrived();                    // this is well defined even in C++03, the object lives until r goes out of scope
  const Contrived *p1 = &r;                             // compiles fine, given the type of r this should be fine. But is it considering r was initialized with an rvalue?
  const Contrived *p2 = &(const Contrived&)Contrived(); // this is handy when calling functions, is it valid? It also compiles
  const int       *p3 = &(const int&)27;                // it works with PODs too, is it valid C++?

  return 0;
}

The three pointer initializations are all more or less the same thing. The question is, are these initializations valid C++ under C++03, C++11, or both? I ask about C++11 separately in case something changed, considering that a lot of work was put in around rvalue references. It may not seem worthwhile to assign these values such as in the above example, but it's worth noting this could save some typing if such values are being passed to a function taking constant pointers and you don't have an appropriate object lying around or feel like making a temporary object on a line above.

EDIT:
Based on the answers the above is valid C++03 and C++11. I'd like to call out some additional points of clarification with regard to the resulting objects' lifetimes.

Consider the following code:

class Contrived {
  int something;
} globalClass;
int globalPOD = 0;

template <typename T>
void SetGlobal(const T *p, T &global) {
  global = *p;
}

int main() {
  const int *p1 = &(const int&)27;
  SetGlobal<int>(p1, globalPOD);              // does *p still exist at the point of this call?

  SetGlobal<int>(&(const int&)27, globalPOD); // since the rvalue expression is cast to a reference at the call site does *p exist within SetGlobal

  // or similarly with a class
  const Contrived *p2 = &(const Contrived&)Contrived();
  SetGlobal<Contrived>(p2, globalClass);
  SetGlobal<Contrived>(&(const Contrived&)Contrived(), globalClass);

  return 0;
}

The question is are either or both of the calls to SetGlobal valid, in that they are passing a pointer to an object that will exist for the duration of the call under the C++03 or C++11 standard?

¿Fue útil?

Solución

An rvalue is a type of expression, not a type of object. We're talking about the temporary object created by Contrived(), it doesn't make sense to say "this object is an rvalue". The expression that created the object is an rvalue expression, but that's different.

Even though the object in question is a temporary object, its lifetime has been extended. It's perfectly fine to perform operations on the object using the identifier r which denotes it. The expression r is an lvalue.

p1 is OK. On the p2 and p3 lines, the lifetime of the reference ends at the end of that full-expression, so the temporary object's lifetime also ends at that point. So it would be undefined behaviour to use p2 or p3 on subsequent lines. The initializing expression could be used as an argument to a function call though, if that's what you meant.

Otros consejos

The first one is good: the expression r is not in fact an rvalue.

The other two are technically valid, too, but be aware that pointers become dangling at the end of the full expression (at the semicolon), and any attempt to use them would exhibit undefined behavior.

While it is perfectly legal to pass an rvalue by const&, you have to be aware that your code ends up with invalidated pointers in p2 and p3, since the lifetime of the objects that they point is over.

To exemplify this, consider the following code that is often used to pass a temporary by reference:

template<typename T>
void pass_by_ref(T const&);

A function like this can be called with an lvalue or rvalue as its argument (and often is). Inside that function you can obviously take the reference of your argument - it is just a reference to a const object after all... You are basically doing the exact same thing without the help of a function.

In fact, in C++11, you can go one step further and obtain a non-const pointer to an temporary:

template<typename T>
typename std::remove_reference<T>::type* example(T&& t)
{
    return &t;
}

Note that the object the return value points to will only still exist if this function is called with an lvalue (since its argument will turn out to be typename remove_reference<T>::type& && which is typename remove_reference<T>::type&).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top