return by value assigned to const reference - c++

I was fixing another bug in some code and came across some code that I would have thought was a bug; however, this code compiles under gcc 4.4, 4.5, and 4.6 and appears to function as "expected". Can anyone tell me if this is valid c++?
struct foo {
int bar;
};
foo myfunction(foo const &orig) {
foo fooOnStack = orig;
fooOnStack.bar *= 100;
return fooOnStack;
}
void myOtherFunction(foo const &orig) {
foo const &retFoo = myfunction();
// perhaps do some tests on retFoo.bar ...
}
If this is valid c++, does anyone know the rationale behind this being legal?

Yes, this is legal C++. Forming a reference-to-const to a temporary extends the lifetime of the temporary to the lifetime of the reference.

I think you still need to be careful. I have a case where g++-4.9 with C++11 and complicated Eigen types does not respect this (it deletes data in the returned temporary even though its lifetime is supposed to be extended). Hence, even though it might be legal it should be flagged as dodgy unless there is a really good reason for it.
Also, given C++11 MUST allocate the temporary at the call site if returned by value its usefulness is perhaps less than it used to be if you are using the latest standard.

Related

Rationale for const ref binding to a different type?

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.

What is const reference of the return value? [duplicate]

I was fixing another bug in some code and came across some code that I would have thought was a bug; however, this code compiles under gcc 4.4, 4.5, and 4.6 and appears to function as "expected". Can anyone tell me if this is valid c++?
struct foo {
int bar;
};
foo myfunction(foo const &orig) {
foo fooOnStack = orig;
fooOnStack.bar *= 100;
return fooOnStack;
}
void myOtherFunction(foo const &orig) {
foo const &retFoo = myfunction();
// perhaps do some tests on retFoo.bar ...
}
If this is valid c++, does anyone know the rationale behind this being legal?
Yes, this is legal C++. Forming a reference-to-const to a temporary extends the lifetime of the temporary to the lifetime of the reference.
I think you still need to be careful. I have a case where g++-4.9 with C++11 and complicated Eigen types does not respect this (it deletes data in the returned temporary even though its lifetime is supposed to be extended). Hence, even though it might be legal it should be flagged as dodgy unless there is a really good reason for it.
Also, given C++11 MUST allocate the temporary at the call site if returned by value its usefulness is perhaps less than it used to be if you are using the latest standard.

Address of return value of method

I have a get-method that returns a value:
int Foo::getValue() const
{
return value_;
}
I'm using the following code to write this value to a binary file:
void Bar::write(const Foo& foo)
{
/* some code... */
file_.write(reinterpret_cast<char*>(&foo.getValue()), sizeof(int));
}
And it seems that everything works well. But I am not sure about that &foo.getValue() thing. Will it always return an address of return value of getValue() or are there any pitfalls?
EDIT1: I dont want to create a temporary var for this.
EDIT2: Yes, it compiles and works fine.
EDIT3: write is a ostream::write
EDIT4: With /Wall and /Za it will not compile: error C2102: '&' requires l-value
The standard forbids you to take the address of an rvalue: https://stackoverflow.com/a/28459180/2642059 A compiler that is not standard compliant may allow this behavior, but assuredly that is not behavior that will be cross platform, nor is it guaranteed to succeed even between updates to the compiler that may more closely align it with the C++ standard.
Since you are not programming for a specific compiler, but for C++, finding something that works in a specific compiler should not be your objective. Finding a way to accomplish this within the standard would be preferable.
A couple easy options for doing that would be:
Turn it into an lvalue by assigning it to a temporary value. They're free! The compiler will optimize them right out
Change the return of getValue to const int& this will allow you to work with an lvalue. Since ints are typically copied by value this probably will not have repercussions in your code base, but it could
Create another method which could directly return (const char*)&value_, you could get in trouble abusing this one though so be careful
Microsoft Compiler for whatever reason has a `"feature" - it implicitly converts temporary to lvalue. You can check it by this code:
int &ref = foo.getValue();
this code as well as your should not compile. Proper code would be:
void Bar::write(const Foo& foo)
{
/* some code... */
int temp = foo.getValue();
file_.write(static_cast<const char*>(&temp), sizeof(int));
}

C++0x unique_ptr misunderstanding?

In N2812 is an example in the Introduction where a unique_ptr is given as a value parameter
void push_back2(
std::list<std::unique_ptr<int>>& l, std::unique_ptr<int> a)
{
l.push_back(a); // oops: moves from the lvalue 'a', silently!
l.push_back(a); // oops: 'a' no longer has its original value
}
The paper discusses a problem with RValue/LValue overload resolution, but thats not my point.
I wonder, if providing the argument std::unique_ptr<int> a by-value is not causing a compiler error? It would copy it, right? And that is not allowed for unique_ptr
I am aware that the paper is quite old, maybe the definition of unique_ptr has changed, since. But maybe it's just a typo and the author wanted to write std::unique_ptr<int> &a instead?
My gcc 4.7.0 agrees with me, but thats no proof :-)
void push_back2( std::list<std::unique_ptr<int>>&, std::unique_ptr<int> ) { };
int main() {
list<unique_ptr<int>> lst;
unique_ptr<int> num { new int{4} };
push_back2(lst, num); //ERR: use of deleted function
}
There's nothing wrong with taking the parameter by value. You are correct that if you try to initialize the parameter by using a copy, you will get a compiler error since that function is deleted. However, you could initialize the value parameter by providing an rvalue as the argument. For example:
std::unique_ptr<int> myPtr{ /* ... */ }
std::list<std::unique_ptr<int>> elems;
push_back2(elems, myPtr); // Error, as you've noted
push_back2(elems, std::move(myPtr)); // Fine, uses move constructor to initialize
This syntax is nice in that it forces you to explicitly indicate that you're handing the pointer over to the function.
Once you're inside push_back2, you are correct that push_back won't be able to take in the unique_ptr because it will try to use the nonexistent copy constructor. To fix this, you'll need to use std::move again:
void push_back2(
std::list<std::unique_ptr<int>>& l, std::unique_ptr<int> a)
{
l.push_back(std::move(a)); // Fine, moves a
l.push_back(std::move(a)); // Well that was dumb, but you asked for it!
}
I hope that I interpreted your question correctly and that this is what you're looking for... let me know if there's anything else I can try to clarify!
Your knowledge and assumptions about the behavior are correct.
The paper's example is confusing as it is conflating two languages: C++ with concepts and C++ without concepts. In the paper's pretend language, the list push_back which requires CopyConstructible is SFINAE'd away, leaving only the overload requiring MoveConstructible. In such a list design, and with the old rules that an lvalue could bind to an rvalue-reference, then the push_back would have implicitly moved from a twice.
Note that we were in no danger at any time of list actually behaving this way. The authors were simply trying to set up a situation where const value_type& and value_type&& were not overloaded, and you had only value_type&& in the overload set.
You can provide it by rvalue- for example, by function return.
unique_ptr<int> make_unique() {
return unique_ptr<int>(new int);
}
int main() {
list<unique_ptr<int>> lst;
lst.push_back(make_unique());
}
In addition, you can explicitly std::move into the list.

Passing non-const references to rvalues in C++

In the following line of code:
bootrec_reset(File(path, size, off), blksize);
Calling a function with prototype:
static void bootrec_reset(File &file, ssize_t blksize);
I receive this error:
libcpfs/mkfs.cc:99:53: error: invalid initialization of non-const reference of type 'File&' from an rvalue of type 'File'
libcpfs/mkfs.cc:30:13: error: in passing argument 1 of 'void bootrec_reset(File&, ssize_t)'
I'm aware that you can not pass non-const references (const &) to rvalues according to the standard. MSVC however allows you to do this (see this question). This question attempts to explain why but the answer makes no sense as he is using references to literals, which are a corner case and should obviously be disallowed.
In the given example it's clear to see that following order of events will occur (as it does in MSVC):
File's constructor will be called.
A reference to the File, and blksize, are pushed on the stack.
bootrec_reset makes use of file.
After returning from bootrec_reset, the temporary File is destroyed.
It's necessary to point out that the File reference needs to be non-const, as it's a temporary handle to a file, on which non-const methods are invoked. Furthermore I don't want to pass the File's constructor arguments to bootrec_reset to be constructed there, nor do I see any reason to manually construct and destroy a File object in the caller.
So my questions are:
What justifies the C++ standard disallowing non-const references in this manner?
How can I force GCC to permit this code?
Does the upcoming C++0x standard change this in anyway, or is there something the new standard gives me that is more appropriate here, for example all that jibberish about rvalue references?
Yes, the fact that plain functions cannot bind non-const references to temporaries -- but methods can -- has always bugged me. TTBOMK the rationale goes something like this (sourced from this comp.lang.c++.moderated thread):
Suppose you have:
void inc( long &x ) { ++x; }
void test() {
int y = 0;
inc( y );
std::cout << y;
}
If you allowed the long &x parameter of inc() to bind to a temporary long copy made from y, this code obviously wouldn't do what you expect -- the compiler would just silently produce code that leaves y unchanged. Apparently this was a common source of bugs in the early C++ days.
Had I designed C++, my preference would have been to allow non-const references to bind to temporaries, but to forbid automatic conversions from lvalues to temporaries when binding to references. But who knows, that might well have opened up a different can of worms...
"What justifies the C++ standard disallowing non-const references in this manner?"
Practical experience with the opposite convention, which was how things worked originally. C++ is to a large degree an evolved language, not a designed one. Largely, the rules that are still there are those that turned out to work (although some BIG exceptions to that occurred with the 1998 standardization, e.g. the infamous export, where the committee invented rather than standardizing existing practice).
For the binding rule one had not only the experience in C++, but also similar experience with other languages such as Fortran.
As #j_random_hacker notes in his answer (which as I wrote this was scored 0, showing that the scoring in SO really doesn't work as a measure of quality), the most serious problems have to do with implicit conversions and overload resolution.
"How can I force GCC to permit this code?"
You can't.
Instead of ...
bootrec_reset(File(path, size, off), blksize);
... write ...
File f(path, size, off);
bootrec_reset(f, blksize);
Or define an appropriate overload of bootrec_reset. Or, if "clever" code appeals, you can in principle write bootrec_reset(tempref(File(path, size, off)), blksize);, where you simply define tempref to return its argument reference appropriately const-casted. But even though that's a technical solution, don't.
"Does the upcoming C++0x standard change this in anyway, or is there something the new standard gives me that is more appropriate here, for example all that jibberish about rvalue references?"
Nope, nothing that changes things for the given code.
If you're willing to rewrite, however, then you can use e.g. C++0x rvalue references, or the C++98 workarounds shown above.
Cheers & hth.,
Does the upcoming C++0x standard change this in anyway, or is there something the new standard gives me that is more appropriate here, for example all that jibberish about rvalue references?
Yes. Since every name is an lvalue, it is almost trivial to treat any expression as if it was an lvalue:
template <typename T>
T& as_lvalue(T&& x)
{
return x;
}
// ...
bootrec_reset(as_lvalue(File(path, size, off)), blksize);
Is a fairly arbitrary decision - non-const references to temporaries are allowed when the temporary is the subject of a method call, for example (e.g. the "swap trick" to free the memory allocated by a vector, std::vector<type>().swap(some_vector);)
Short of giving the temporary a name, I don't think you can.
As far as I'm aware this rule exists in C++0x too (for regular references), but rvalue references specifically exist so you can bind references to temporaries - so changing bootrec_reset to take a File && should make the code legal.
Please note that calling C++0x "jibberish" is not presenting a very favorable picture of your coding ability or desire to understand the language.
1) Is actually not so arbitrary. Allowing non-const references to bind to r-values leads to extremely confusing code. I recently filed a bug against MSVC which relates to this, where the non-standard behavior caused standard-compliant code to fail to compile and/or compile with a deviant behavior.
In your case, consider:
#include <iostream>
template<typename T>
void func(T& t)
{
int& r = t;
++r;
}
int main(void)
{
int i = 4;
long n = 5;
const int& r = n;
const int ci = 6;
const long cn = 7;
//int& r1 = ci;
//int& r2 = cn;
func(i);
//func(n);
std::cout << r << std::endl;
}
Which of the commented lines to you want to compile? Do you want func(i) to change its argument and func(n) to NOT do so?
2) You can't make that code compile. You don't want to have that code. A future version of MSVC is probably going to remove the non-standard extension and fail to compile that code. Instead, use a local variable. You can always use a extra pair of braces to control the lifetime of that local variable and cause it to be destroyed prior to the next line of code, just like the temporary would be. Or r-value references.
{
File ftemp(path, size, off);
bootrec_reset(ftemp, blksize);
}
3) Yes, you can use C++0x r-value references in this scenario.
Alternatively, simply overload.
static void bootrec_reset(File &&file, ssize_t blksize) {
return bootrec_reset(file, blksize);
}
This is the easiest solution.
How can I force GCC to permit this code?
If you own the definition of File then you can try playing tricks such as this one:
class File /* ... */ {
public:
File* operator&() { return this; }
/* ... */
};
/* ... */
bootrec_reset(*&File(path, size, off), blksize);
This compiles for me in c++98 mode.
Does the upcoming C++0x standard change this in anyway, or is there something the new standard gives me that is more appropriate here, for example all that jibberish about rvalue references?
Obviously this the way to go if at all possible.