This question already has answers here:
What are the use cases for having a function return by const value for non-builtin type?
(4 answers)
Closed 8 years ago.
What is the purpose of the const in this?
const Object myFunc(){
return myObject;
}
I've just started reading Effective C++ and Item 3 advocates this and a Google search picks up similar suggestions but also counterexamples. I can't see how using const here would ever be preferable. Assuming a return by value is desirable, I don't see any reason to protect the returned value. The example given for why this might be helpful is preventing unintended bool casts of the return value. The actual problem then is that implicit bool casts should be prevented with the explicit keyword.
Using const here prevents using temporary objects without assignment. So I couldn't perform arithmetic expressions with those objects. It doesn't seem like there's ever a case that an unnamed const is useful.
What is gained by using const here and when would it be preferable?
EDIT: Change arithmetic example to any function that modifies an object that you might want to perform before an assignment.
In the hypothetical situation where you could perform a potentially expensive non-const operation on an object, returning by const-value prevents you from accidentally calling this operation on a temporary. Imagine that + returned a non-const value, and you could write:
(a + b).expensive();
In the age of C++11, however, it is strongly advised to return values as non-const so that you can take full advantage of rvalue references, which only make sense on non-constant rvalues.
In summary, there is a rationale for this practice, but it is essentially obsolete.
It's pretty pointless to return a const value from a function.
It's difficult to get it to have any effect on your code:
const int foo() {
return 3;
}
int main() {
int x = foo(); // copies happily
x = 4;
}
and:
const int foo() {
return 3;
}
int main() {
foo() = 4; // not valid anyway for built-in types
}
// error: lvalue required as left operand of assignment
Though you can notice if the return type is a user-defined type:
struct T {};
const T foo() {
return T();
}
int main() {
foo() = T();
}
// error: passing ‘const T’ as ‘this’ argument of ‘T& T::operator=(const T&)’ discards qualifiers
it's questionable whether this is of any benefit to anyone.
Returning a reference is different, but unless Object is some template parameter, you're not doing that.
It makes sure that the returned object (which is an RValue at that point) can't be modified. This makes sure the user can't do thinks like this:
myFunc() = Object(...);
That would work nicely if myFunc returned by reference, but is almost certainly a bug when returned by value (and probably won't be caught by the compiler). Of course in C++11 with its rvalues this convention doesn't make as much sense as it did earlier, since a const object can't be moved from, so this can have pretty heavy effects on performance.
It could be used as a wrapper function for returning a reference to a private constant data type. For example in a linked list you have the constants tail and head, and if you want to determine if a node is a tail or head node, then you can compare it with the value returned by that function.
Though any optimizer would most likely optimize it out anyway...
Related
I recently learned that it's possible to assign a value to a reference of a different type. Concrete example:
const std::optional<float>& ref0 = 5.0f;
const std::optional<float>& ref1 = get_float();
That's surprising to me. I would certainly expect this to work with a non-reference, but assumed that references only bind to the same type.
I found a pretty good chunk of the c++ standard which talks about all kinds of ways this works: https://eel.is/c++draft/dcl.init.ref#5. But I would appreciate some insight: When is this ever desirable?
A particular occasion where this hurt me recently was this:
auto get_value() -> std::optional<float>{ /* ... */ }
const std::optional<float>& value = get_value();
// check and use value...
I later then changed the return value of the function to a raw float, expecting all uses with a reference type to fail. They did not. Without paying attention, all the useless checking code would have stayed in place.
The basic reason is one of consistency. Since const-reference parameters are very widely used not for reference semantics but merely to avoid copying, one would expect each of
void y(X);
void z(const X&);
to accept anything, rvalue or otherwise, that can be converted to an X. Initializing a local variable has the same semantics.
This syntax also once had a practical value: in C++03, the results of functions (including conversions) were notionally copied:
struct A {A(int);};
struct B {operator A() const;};
void g() {
A o=B(); // return value copied into o
const A &r=3; // refers to (lifetime-extended) temporary
}
There was already permission to elide these copies, and in this sort of trivial case it was common to do so, but the reference guaranteed it.
This question already has answers here:
What are the use cases for having a function return by const value for non-builtin type?
(4 answers)
Closed 8 years ago.
What is the purpose of the const in this?
const Object myFunc(){
return myObject;
}
I've just started reading Effective C++ and Item 3 advocates this and a Google search picks up similar suggestions but also counterexamples. I can't see how using const here would ever be preferable. Assuming a return by value is desirable, I don't see any reason to protect the returned value. The example given for why this might be helpful is preventing unintended bool casts of the return value. The actual problem then is that implicit bool casts should be prevented with the explicit keyword.
Using const here prevents using temporary objects without assignment. So I couldn't perform arithmetic expressions with those objects. It doesn't seem like there's ever a case that an unnamed const is useful.
What is gained by using const here and when would it be preferable?
EDIT: Change arithmetic example to any function that modifies an object that you might want to perform before an assignment.
In the hypothetical situation where you could perform a potentially expensive non-const operation on an object, returning by const-value prevents you from accidentally calling this operation on a temporary. Imagine that + returned a non-const value, and you could write:
(a + b).expensive();
In the age of C++11, however, it is strongly advised to return values as non-const so that you can take full advantage of rvalue references, which only make sense on non-constant rvalues.
In summary, there is a rationale for this practice, but it is essentially obsolete.
It's pretty pointless to return a const value from a function.
It's difficult to get it to have any effect on your code:
const int foo() {
return 3;
}
int main() {
int x = foo(); // copies happily
x = 4;
}
and:
const int foo() {
return 3;
}
int main() {
foo() = 4; // not valid anyway for built-in types
}
// error: lvalue required as left operand of assignment
Though you can notice if the return type is a user-defined type:
struct T {};
const T foo() {
return T();
}
int main() {
foo() = T();
}
// error: passing ‘const T’ as ‘this’ argument of ‘T& T::operator=(const T&)’ discards qualifiers
it's questionable whether this is of any benefit to anyone.
Returning a reference is different, but unless Object is some template parameter, you're not doing that.
It makes sure that the returned object (which is an RValue at that point) can't be modified. This makes sure the user can't do thinks like this:
myFunc() = Object(...);
That would work nicely if myFunc returned by reference, but is almost certainly a bug when returned by value (and probably won't be caught by the compiler). Of course in C++11 with its rvalues this convention doesn't make as much sense as it did earlier, since a const object can't be moved from, so this can have pretty heavy effects on performance.
It could be used as a wrapper function for returning a reference to a private constant data type. For example in a linked list you have the constants tail and head, and if you want to determine if a node is a tail or head node, then you can compare it with the value returned by that function.
Though any optimizer would most likely optimize it out anyway...
I have two versions of the same static member function: one takes a pointer-to-const parameter and that takes a pointer-to-non-const parameter. I want to avoid code duplication.
After reading some stack overflow questions (these were all about non-static member functions though) I came up with this:
class C {
private:
static const type* func(const type* x) {
//long code
}
static type* func(type* x) {
return const_cast<type*>(func(static_cast<const type*>(x)));
}
public:
//some code that uses these functions
};
(I know juggling with pointers is generally a bad idea, but I'm implementing a data structure.)
I found some code in libstdc++ that looks like this:
NOTE: these are not member functions
static type* local_func(type* x)
{
//long code
}
type* func(type* x)
{
return local_func(x);
}
const type* func(const type* x)
{
return local_func(const_cast<type*>(x));
}
In the first approach the code is in a function that takes a pointer-to-const parameter.
In the second approach the code is in a function that takes a pointer-to-non-const parameter.
Which approach should generally be used? Are both correct?
The most important rule is that an interface function (public method, a free function other than one in a detail namespace, etc), should not cast away the constness of its input. Scott Meyer was one of the first to talk about preventing duplication using const_cast, here's a typical example (How do I remove code duplication between similar const and non-const member functions?):
struct C {
const char & get() const {
return c;
}
char & get() {
return const_cast<char &>(static_cast<const C &>(*this).get());
}
char c;
};
This refers to instance methods rather than static/free functions, but the principle is the same. You notice that the non-const version adds const to call the other method (for an instance method, the this pointer is the input). It then casts away constness at the end; this is safe because it knows the original input was not const.
Implementing this the other way around would be extremely dangerous. If you cast away constness of a function parameter you receive, you are taking a big risk in UB if the object passed to you is actually const. Namely, if you call any methods that actually mutate the object (which is very easy to do by accident now that you've cast away constness), you can easily get UB:
C++ standard, section § 5.2.11/7 [const cast]
[ Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer to data member resulting from a
const_cast that casts away a const-qualifier may produce undefined
behavior. —end note ]
It's not as bad in private methods/implementation functions because perhaps you carefully control how/when its called, but why do it this way? It's more dangerous to no benefit.
Conceptually, it's often the case that when you have a const and non-const version of the same function, you are just passing along internal references of the object (vector::operator[] is a canonical example), and not actually mutating anything, which means that it will be safe either way you write it. But it's still more dangerous to cast away the constness of the input; although you might be unlikely to mess it up yourself, imagine a team setting where you write it the wrong way around and it works fine, and then someone changes the implementation to mutate something, giving you UB.
In summary, in many cases it may not make a practical difference, but there is a correct way to do it that's strictly better than the alternative: add constness to the input, and remove constness from the output.
I have actually only ever seen your first version before, so from my experience it is the more common idiom.
The first version seems correct to me while the second version can result in undefined behavior if (A) you pass an actual const object to the function and (B) the long code writes to that object. Given that in the first case the compiler will tell you if you're trying to write to the object I would never recommend option 2 as it is. You could consider a standalone function that takes/returns const however.
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!
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&).