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.
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.
I am a C++ newbie and I am struggling on the temporaries topic. I don't find anywhere a clear list of all cases in which the compiler will create a temporary. Actually, a few days ago I had in mind that when we pass an rvalue as const reference parameter, then the compiler will create a temporary and will put the rvalue into that temporary to hold it, passing the temporary as the parameter. Giving this example to my teacher,
void func(const int& _param){}
int main()
{
func(10);
// int __tmp__ = 10;
// func(__tmp__);
return 0
}
he said in this case the compiler won't create a temporary, but instead will directly optimize the code replacing 10 in all the occurrences inside the function body, so after that, I am more confused than ever.
If you are referring to the language concept rather than what the compiler ends up doing, a temporary in simple terms is a value that ends up arising from some expression and is not bound to any variable. Its value is usually used for some intermediate computation, and then discarded. Here is a somewhat contrived example.
std::string x = "foo";
std::string y = "bar";
auto length = (x + std::to_string(some_number) + y).size();
// __TEMP0__ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// __TEMP1__ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// __TEMP2__ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The result of std::to_string(some_number) is not assigned to anything as it is being used, so that alone is a temporary. It is used in the [sub]expression x + std::to_string(some_number), which creates another temporary string, which itself is +'d with y, creating yet another temporary, whose size is taken and assigned to length. In this case all temporaries involve will exist0 until the end of the evaluation of the whole expression, and then discarded. It'll be as if the code were instead something like this.
std::string x = "foo";
std::string y = "bar";
std::string __TEMP0__ = std::to_string(some_number);
std::string __TEMP1__ = x + __TEMP0__;
std::string __TEMP2__ = __TEMP1__ + y;
auto length = __TEMP2__.size();
Except that the temporaries remain as rvalues and will be destroyed after the evaluation of __TEMP2__.size().
0 Well, it'll be as if they existed. Who knows what your compiler will do to optimize it out.
The "as-if" rule applies here. See https://en.cppreference.com/w/cpp/language/as_if.
Your job is to specify your intention by writing some C++ source code. The compiler's job is to turn that into machine code that matches the intention.
There's nothing in the C++ standard to tell you how many temporaries are created in your program. A good compiler will optimise your program to
int main(){}
If the creation of the temporary has an observable side-effect then the compiler's options are a little more restricted. But in NRVO and RVO circumstances it is allowed to ignore that. In later C++ standards it is required to.
instead will directly optimize the code replacing 10 in all the occurrences inside the function body
It's up to the compiler whether or not (and how) to optimize this, or anything else. Except for certain exceptions (e.g. copy elision), optimizations are allowed if and only if they don't affect the observable behavior ("as-if rule" mentioned in the other answer).
when we pass an rvalue as const reference parameter, then the compiler will create a temporary and will put the rvalue into that temporary to hold it, passing the temporary as the parameter
Depends on the kind of rvalue. 10 is a prvalue, which are not even objects starting with C++17. References can't bind to prvalues directly, so it's materialized into an xvalue first, which is a temporary.
If you passed an xvalue, the reference would bind directly to it.
The above applies to C++17, I'm not sure how things worked before.
I have the following code:
std::vector<Info*> filter(int direction)
{
std::vector<Info*> new_buffer;
for(std::vector<Info*>::iterator it=m_Buffer.begin();it<m_Buffer.end();it++)
{
if(((*it)->direction == direction)
{
new_buffer.push_back(*it);
}
}
return new_buffer;
}
std::vector<Info*> &filteredInfo= filter(m_Direction);
Can someone explain what is happening here ? Would the filter method return by value create a temporary and filteredInfo never gets destroyed because its a reference ?
Not sure if I understand correctly. What is the diference between filteredInfo being a reference and not being one in this case ?
Your compiler should complain of that code.
This statement:
std::vector<Info*> &filteredInfo= filter(m_Direction);
is a bad idea where filter is:
std::vector<Info*> filter(int direction);
You are trying to create a reference to a temporary object. Even if it succeeds with your compiler, its illegal.
You should use:
std::vector<Info*> filteredInfo= filter(m_Direction);
Its as efficient as you want. Either a move operation (C++11) will happen there or Return Value Optimization will kick in. For your implementation of filter, it should be RVO on optimized builds (it depends on your compiler quality though) .
However, you should note that you are copying raw pointers into your vector, I hope you have a correct ownership model? If not, I advice you to use a smart pointer.
Here is what happens:
std::vector<Info*> new_buffer; creates an object locally.
return new_buffer; moves new_buffer to a temporary object when filter(m_Direction) is called.
Now if you call std::vector<Info*> filteredInfo= filter(m_Direction); the temprary object will be moved to filteredInfo so there is no unnecessary copies and it's the most efficient way.
But, if you call std::vector<Info*> &filteredInfo= filter(m_Direction); then filteredInfo is bound to a temporary object, which is a terrible idea and most compilers will complain about this.
Here you're correctly puzzled because there are two independent weird facts mixing in:
Your compiler allows a non-const reference to be bound to a temporary. This historically was a mistake in Microsoft compilers and is not permitted by the standard. That code should not compile.
The standard however, strangely enough, actually allows binding const references to temporaries and has a special rule for that: the temporary object will not be destroyed immediately (like it would happen normally) but its life will be extended to the life of the reference.
In code:
std::vector<int> foo() {
std::vector<int> x{1,2,3};
return x;
}
int main() {
const std::vector<int>& x = foo(); // legal
for (auto& item : x) {
std::cout << x << std::endl;
}
}
The reason for this apparently absurd rule about binding const references to temporaries is that in C++ there is a very common "pattern"(1) of passing const references instead of values for parameters, even when identity is irrelevant. If you combine this (anti)-pattern with implicit conversion what happens is that for example:
void foo(const std::string& x) { ... }
wouldn't be callable with
foo("Hey, you");
without the special rule, because the const char * (literal) is implicitly converted to a temporary std::string and passed as parameter bound to a const reference.
(1) The pattern is indeed quite bad from a philosophical point of view because a value is a value and a reference is a reference: the two are logically distinct concepts. A const reference is not a value and confusing the two can be the source of very subtle bugs. C++ however is performance-obsessed and, especially before move semantics, passing const references was considered a "smart" way of passing values, despite being a problem because of lifetime and aliasing issues and for making things harder for the optimizer. With a modern compiler passing a reference should be used only for "big" objects, especially ones that are not constructed on the fly to be passed or when you're actually interested in object identity and not in just object value.
In C++, the lifetime of a temporary value can be extended by binding it to a reference:
Foo make_foo();
{
Foo const & r1 = make_foo();
Foo && r2 = make_foo();
// ...
} // both objects are destroyed here
Why is this allowed? What problem does this solve?
I couldn't find an explanation for this in Design and Evolution (e.g. 6.3.2: Lifetime of Temporaries). Nor could I find any previous questions about this (this one came closest).
This feature is somewhat unintuitive and has subtle failure modes. For example:
Foo const & id(Foo const & x) { return x; } // looks like a fine function...
Foo const & r3 = id(make_foo()); // ... but causes a terrible error!
Why is something that can be so easily and silently abused part of the language?
Update: the point may be subtle enough to warrant some clarification: I do not dispute the use of the rule that "references bind to temporaries". That is all fine and well, and allows us to use implicit conversions when binding to references. What I am asking about is why the lifetime of the temporary is affected. To play the devil's advocate, I could claim that the existing rules of "lifetime until end of full expression" already cover the common use cases of calling functions with temporary arguments.
The simple answer is that you need to be able to bind a temporary with a const reference, not having that feature would require a good amount of code duplication, with functions taking const& for lvalue or value arguments or by-value for rvalue arguments.
Once you need that the language needs to define some semantics that will guarantee the lifetime of the temporary is at least as long as that of the reference.
Once you accept that a reference can bind to an rvalue in one context, just for consistency you may want to extend the rule to allow the same binding in other contexts, and the semantics are really the same. The temporary lifetime is extended until the reference goes away (be it a function parameter, or a local variable).
The alternative would be rules that allow binding in some contexts (function call) but not all (local reference) or rules that allow both and always create a dangling reference in the latter case.
Removed the quote from the answer, left here so that comments would still make sense:
If you look at the wording in the standard there are some hints as of this intended usage:
12.2/5 [middle of the paragraph]
[...] A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call. [...]
As Bjarne Stroustrup (the original designer) explained it in a clc++ posting in 2005, it was for uniform rules.
The rules for references are simply the most general and uniform I
could find. In the cases of arguments and local references, the
temporary lives as long as the reference to which it is bound. One
obvious use is as a shorthand for a complicated expression in a
deeply nested loop. For example:
for (int i = 0; i<xmax; ++i)
for (int j = 0; j< ymax; ++j) {
double& r = a[i][j];
for (int k = 0; k < zmax; ++k) {
// do something with a[i][j] and a[i][j][k]
}
}
This can improve readability as well as run-time performance.
And it turned out to be useful for storing an object of a class derived from the reference type, e.g. as in the original Scopeguard implementation.
In a clc++ posting in 2008, James Kanze supplied some more details:
The standard says exactly when the destructor must be called. Before
the standard, however, the ARM (and earlier language specifications)
were considerably looser: the destructor could be called anytime after
the temporary was "used" and before the next closing brace.
(The “ARM” is the Annotated Reference Manual by (IIRC) Bjarne Stroustrup and Margareth Ellis, which served as a de-facto standard in the last decade before the first ISO standard. Unfortunately my copy is buried in a box, under a lot of other boxes, in the outhouse. So I can't verify, but I believe this is correct.)
Thus, as with much else the details of lifetime extensions were honed and perfected in the standardization process.
Since James has raised this point in comments to this answer: that perfection could not reach back in time to affect Bjarne's rationale for the lifetime extension.
Example of Scopeguard-like code, where the temporary bound to the reference is the full object of derived type, with its derived type destructor executed at the end:
struct Base {};
template< class T >
struct Derived: Base {};
template< class T >
auto foo( T ) -> Derived<T> { return Derived<T>(); }
int main()
{
Base const& guard = foo( 42 );
}
I discovered an interesting application for lifetime extension somewhere here on SO. (I forget where, I'll add a reference when I find it.)
Lifetime extension allows us to use prvalues of immobile types.
For example:
struct Foo
{
Foo(int, bool, char);
Foo(Foo &&) = delete;
};
The type Foo cannot be copied nor moved. Yet, we can have a function that returns a prvalue of type Foo:
Foo make_foo()
{
return {10, false, 'x'};
}
Yet we cannot construct a local variable initialized with the return value of make_foo, so in general, calling the function will create a temporary object that is immediately destroyed. Lifetime extension allows us to use the temporary object throughout an entire scope:
auto && foo = make_foo();
Please consider the three functions.
std::string get_a_string()
{
return "hello";
}
std::string get_a_string1()
{
return std::string("hello");
}
std::string get_a_string2()
{
std::string str("hello");
return str;
}
Will RVO be applied in all the three cases?
Is it OK to return a temporary like in the above code? I believe it is OK since I am returning it by value rather than returning any reference to it.
Any thoughts?
In two first cases RVO optimization will take place. RVO is old feature and most compilers supports it. The last case is so called NRVO (Named RVO). That's relatively new feature of C++. Standard allows, but doesn't require implementation of NRVO (as well as RVO), but some compilers supports it.
You could read more about RVO in Item 20 of Scott Meyers book More Effective C++. 35 New Ways to Improve Your Programs and Designs.
Here is a good article about NRVO in Visual C++ 2005.
First, it's completely okay to return a temporary by value which is what you do. It is copied and though the original will go out of scope the copy will not do so and can be safely used by the caller.
Second, all three cases are in fact identical (since you don't access the temporary in the third case anyway) and a compiler might even emit the same code for all of them. Hence it can use RVO in all three cases. This is entirely compiler-dependent.
All cases are correct. They all will construct a temporary and apply the copy constructor of the return type. Necessarily, if there is no copy constructor, the code will fail.
RVO will happen on all three cases under most compilers. Only difference is the last one where the standard does not force it. This because you have a named variable. But most compilers are smart enough to apply RVO to it still... the later the named variable is declared and the less transformations it is applied to, the better odds for RVO to be applied to a named variable.
Incidentally, returning a reference is of course possible as you might have seen in other code. What you must not do is return a reference t a local object.
std::string& get_a_string2()
{
std::string str("hello");
return str; //error!
}
Will produce a compile time error, as you know. However,
std::string& get_a_string2(std::string& str)
{
// do something to str
return str; //OK
}
Will work just fine. On this case, there's no construction or copy construction involved. Simply the function returns a reference to its argument.
It depends on your compiler - what platform are you referring to? Best way to find out is to compile a very small test app and check the ASM your compiler produces.
Yes, it is OK, although you never mention what you're concerned about; speed? style? you can a local temporary to a const reference - the lifetime of the temporary will be extended to the lifetime of the reference - try it and see for yourself! (Herb Sutter exaplins this here) See end of post for example.
IMO you're almost always better off trusting your compiler to optimise your code for you. There are very few cases where you should need to care about this sort of thing (very low level code is one such area, where you're interactive with hardware registers).
int foo() { return 42; }
int main(int, char**)
{
const int &iRef = foo();
// iRef is valid at this point too!
}