Consider the following template class
template <typename T>
class A {
public:
virtual T const& getContent() = 0;
};
I derive this class using 'int*' as type 'T', as follows
class A1 : public A<int*> {
private:
int a;
public:
int* const& getContent() { return &a; }
};
I got the following warning: 'returning reference to local temporary object'.
Questions:
Does the compiler implicitly instantiate a local temporary object of type 'int * const' from '&a' before returning its reference?
As I do know that A.a really exists, then can I just ignore this warning? Will there be any undesirable side-effects of using this code?
What is the proper way of handling this situation? Do I need to work with the member variable 'int *a' instead. This would be cumbersome.
Yes.
You cannot ignore this warning. A.a exists, but that's not the issue. What you are returning is not a pointer to int, but a reference to a pointer to int, i.e. it is a double indirection. To be specific, a temporary int* was created inside getContent that pointed to A.a. A reference to that temporary was returned, and then the temporary was destroyed. Using the result of getContent will be undefined behavior.
The idiomatic way to handle this situation would typically be to store the member you are passing a const reference to. In other words, have a member int* a, and then simply return a in your function. Returning a const reference is a common way to expose the fully functionality of a data member without allowing the user of the class to mutate it, messing up your class' invariants.
This is incorrect:
T const& getContent() = 0;
You can do:
T const getContent() = 0;
int* const getContent() { return &a; }
First of all...
int* const&
Is the same as...
int *const&
Edit: Originally, I wrongly stated it was equivalent to const int*&. Sorry, that was my fault.
And, references to pointers are more "cumbersome" than pointer themselves (at least IMHO), don't you think so? There's a reason pointers and references shall never be mixed (unless it's a consecuence of template instantiation, of course)...
int const&*
This is impossible, invalid, and last but no least, insane. Although there's no practical way for references to be implemented other than by pointers, they are not objects, in the sense that they (themselves) don't have a sizeof that does not equals that of their referenced type, and the fact that they don't have address at all, at least as far as the Sacred Writings of N4296 are concerned.
But... pointers are objects! The expression &a returns a rvalue (that is basically an object without an address) of type int*. Now, the statement return &a; will take that value and wrap it up in a reference, because...
That's what the function return type expects.
The lifetime of an rvalue of type T can be extended by means of it being holded in a reference of type const T&. However, you can't use this to return references to temporary rvalues.
Now, because all this stuff implies that you're returning a reference to a temporary rvalue in an unallowed way, you where tempted and falled into Undefined Behaviour.
The solution? Simply don't use references to pointers at all in the first place...
int const *getContent() { return a; }
Or, if you prefer it, use references, but not pointers...
int const &getContent() { return *a; }
Now, to the questions!
*Does the compiler implicitly instantiate a local temporary object of type 'int * const' from '&a' before returning its reference?*: Yes and no. It does instantiate such a temporary as explained above (but usually optimized away), but it's type is int*, not int *const, although the latter gets implicitly casted into the former.
As I do know that A.a really exists, then can I just ignore this warning? Will there be any undesirable side-effects of using this code?: That depends on whether you would ignore a divide-by-zero warning.
What is the proper way of handling this situation? Do I need to work with the member variable 'int *a' instead. This would be cumbersome.: You may either use "cumbersome" pointers or "beatiful" references, see above.
I hope this has led some light on you!
Related
In short: Does the following code have Undefined Behavior, or is this fine?
struct X
{
X(int b)
: value(b)
, ref(value)
{
}
int value;
int& ref;
void isThisUB() const
{
ref = 1;
}
};
int main()
{
const X x(2);
// Is either of these fine?
x.isThisUB();
x.ref = 3;
return x.value;
}
https://godbolt.org/z/1TE9a7M4a
X::value is const for x. According to my understanding of const semantics, this means that modifying it in any way is UB. Yet we can take a non-const reference to it in the constructor and then modify it through that, either in a const member function or directly.
The C++ (at least 17) standard gives an example of const-related UB in [dcl.type.cv] that looks mostly the same, except it employs const_cast. Note how p->x.j = 99 is denoted as UB. I do not see a fundamental difference between achieving this with const_cast vs my above code.
So, is the code above UB? Are non-const reference members/pointers really this big of a footgun?
(If you can come up with search keywords that yield a related question and not just random const stuff, I'll be mighty impressed.)
Does the following code have Undefined Behavior, or is this fine?
It has UB. Standard says:
[dcl.type.cv]
Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.
x is const and you modify its non-mutable member.
I do not see a fundamental difference between achieving this with const_cast vs my above code.
Indeed. Both are UB for the same reason.
Are non-const reference members/pointers really this big of a footgun?
The trigger for the footgun is the issue that the object is temporarily non-const while it is within its constructor. Hence pointers and references to non-const "this" and its subobjects are readily available wthin the constructor regardless of whether the object is going to be const or not. Thus we can conclude that storing those pointers/references for later use is ill-advised.
Storing pointers and references as members referring to "this" are a footgun for several other reasons as well. They require storage that's otherwise unnecessary if you were to access the referred member through its name directly. Furthermore, you'll find that the copy-semantics of the class will likely not be what you had in mind.
If you want to point to a member out of several alternatives, then use a member-pointer, not an object pointer / reference (using storage cannot be avoided for such case). This solves both copying and accidental const violation.
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?
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.
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&).
I was going over the following example
const int a = 12;
int b;
b = const_cast<int&>(a);
and I wanted to know what & in the template parameter type signifies above and why it wont work without the & ?
Update:
Let me rephrase my question . I understand its a reference but what variable is it referring to ? For instance here it states that incase of pointers it references the original (uncast) pointer. I want to know what it references incase of non pointer types and how can b b a reference when it wasnt declared as a reference ?
const_cast is not a template, but rather a type cast. What appears to be a template argument is the destination type for the cast, and in this case it means that you want to obtain a non-const reference to int that refers to the same objects as a.
a is const int& when you do the const_cast as you wrote it.
You can only modify cv-qualifiers of pointer and reference types with const_cast, not of values. This is because specifying constness for rvalues only makes sense if this is of reference or pointer type and can thus be modified.
So if you just want the (non-const) value of the variable a, simply write
b = a;
as the const-ness is ignored anyway. b is then copy-constructed from a.
Basically a is const, b is not const
so const_cast basically says b is stored at a but removes the const.
Take the following example. I create a function pointer named s, set it to f and call it. This compiles fine of course:
void f() {}
int main() {
void (*s)();
s = f;
s();
}
But take this next example, where I declare s now as a "function reference" (if it's so called) and set to f inline. This compiles fine as well:
void f() {}
int main() {
void (&s)() = f;
s();
}
What are the differences between these two ways to create and initialize a function-pointer? Note that when I use the reference syntax I am required to initialize it "in-line" to f whereas with the "pointer" syntax I had the ability to do it both ways. Can you explain that as well? And with that, can you explain what their differences are in terms of usability, and when must I use one form over the other?
Fundamentally the calling side has no distinct difference. But the decl side definitely does. As you have pointed out, references must be initialized to reference something. This makes them "safer", but even then there is no guarantee of "safety".
The function pointer need NOT point to a function at all. It could be NULL, or even uninitialized (pointing to garbage). Not that it matters, because you can always change it later (something you can NOT do with references).
void (*s)(); // uninitialized
or
void (*s)() = NULL; // set to null
and later
void foo()
{
}
s = foo;
You can do none of those with a reference. The reference must be initialized to something and preferabley something valid:
void (&f)() = foo; // ok. also references foo().
void (&f)() = *s; // also ok, but wait, is s valid?? does it have to be??
However, even here a function reference is not guaranteed to be safe, just safer. You can certainly do this:
void (*s)();
void (&f)() = *s;
You may get a compiler warning out of this (I do, "s used before being initialized") but in the end f still is now a reference to a "function" that isn't a function at all; just the random stack garbage in the s pointer. Worse, since references cannot be reassigned. this thing will always point at garbage.
The differences are the same as for any pointer/reference.
References must be initialized and cannot be reassigned later:
int i,j;
int &r = i;
r = j; // i = j, not &r == &j
References cannot be treated as objects distinct from the object they reference (as opposed to pointers, which are objects distinct from the object they point at)...
int i;
int *p = &i; // &p != &i
int &r = i; // &r == &i
Using a function pointer looks syntactically the same as using a reference, but that's because of a special rule with function pointers that allows you to use them without dereferencing them.
You said it yourself, a difference is that with a reference, you have to bind it upon declaration, which guarantees that references always refer to valid objects.
Another difference is that references cannot be rebinded after they are declared, so they refer to one and only one object throughout their lives.
Other than that they are the same thing.
I have met some purists that prefer references and said that pointers are a vestige of C that shouldn't be used.
Other people prefer pointers because they are more "explicit" about the fact that they are pointers.
Whether using one or the other depends on your needs. The way to choose is, use a reference if possible, but if you really need to be able to point it to a different function, then use a pointer.
A reference to a type P is a lot like a const pointer to a type P (not a pointer to a const P, which is different).
As it happens most of the ways they differ are not important if your type P is a function type. & behaves slightly differently, you can directly assign the pointer to a non const pointer, and functions that take one may not take the other.
If the type P was not a function type there would be loads of other differences -- operator=, lifetime of temporaries, etc.
In short, the answer is 'not much'.
Function identifiers are expressions of function type, but they implicitly convert to pointer-to-function type or reference-to-function type. So they can be passed to constructor of either reference or pointer and to operator= of pointer.
Since references syntactically behave like instances, there is no way to act on the reference rather than the referred object. That's why they can only be initialized. By the way prefered syntax in C++ is with parenthesis, not =.
You should use reference when possible and pointers only if you can't use reference. The reason is that since many things can't be done to reference (pointing to NULL, changing referred object, deleting it etc.) there is fewer things you have to look for when reading the code. Plus it saves some * and & characters.
I've got a function which returns an object of type Foo:
Foo getFoo();
I know the following will compile and will work, but why would I ever do it?
const Foo& myFoo = getFoo();
To me, the following is much more readable, and doesn't force me to remember that C++ allows me to assign an r-value to a const reference:
const Foo myFoo = getFoo();
What are the differences between the two? Why would I use the first over the second? Why would I use the second over the first?
Contrary to popular opinion, there is no guarantee that assigning the result of a function returning an object by value to a const reference will result in fewer copies than assigning it to the object itself.
When you assign an rvalue to a const reference, the compiler may bind the reference in one of two ways. It may create a new temporary by copying the rvalue and bind the reference to that, or it may bind the reference directly to the rvalue itself.
If the compiler is not able to make the 'obvious' optimization to remove the temporary and elide the copy constructor for the return value of getFoo, then how likely is it to be able to do the more efficient form of binding an rvalue to a const reference without making a new temporary?
One reason to use a const reference would be to make the function more robust against potential slicing. If the return type were actually a type derived from Foo, then assigning to a base class const reference would be guaranteed not to slice, even if the compiler did make a temporary object from the rvalue returned by the function. The compiler will also generate the correct call to the derived class destructor whether or not the destructor in the base class is virtual or not. This is because the type of the temporary object created is based on the type of the expression being assigned and not on the type of the reference which is being initialized.
Note that the issue of how many copies of the return value are made is entirely separate from the return value optimization and the named return value optimization. These optimizations refer to eliminating the copy of either the rvalue result of evaluating a return expression or of a named local variable into the return value of a function in the body of the function itself. Obviously, in the best possible case, both a return value optimization can be made and the temporary for the return value can be eliminated resulting in no copies being performed on the returned object.
I think GotW #88 answers this best
It is valid to allow this sort of pattern:
void foo(const SomeType &);
foo(SomeType(bar))
In this case, a temporary SomeType is constructed and passed to foo. Most likely, the fact that you can also have constant references on the stack to temporaries is a side effect of the verbiage used to define this behavior in the standard. Note that, as onebyone mentioned in the comments, the temporary's lifetime is extended to be that of the reference itself.
There could be several reasons:
what if you don't want a const object referred to?
For example this won't work:
const Foo &myFoo = getFoo();
myFoo.myfield = x;
Or, what if you are returning a temp object from getFoo()? This will warn about returning a reference (or address) to a local:
const Foo &getFoo(void)
{
Foo localFoo();
// do the things you want to localFoo
return( localFoo );
}
the internals of const Foo& myFoo = getFoo() do pretty much the same thing as
Foo myFoo = getFoo() so the argument that there is performance value to the const ref return are invalid. I find it no problem to return objects of reasonable size.
Disclaimer - I did not try these examples on gcc. Your mileage may vary accordingly.