Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it valid C++ to cast an rvalue to a const pointer?

Tags:

c++

c++11

c++03

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?

like image 815
Apriori Avatar asked Dec 15 '22 23:12

Apriori


1 Answers

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.

like image 196
M.M Avatar answered Jan 07 '23 15:01

M.M