Can someone help me to understand why the following code causes a warning
struct A
{
A() : _a( 0 ) {}
const int& _a;
};
int main()
{
A a;
}
with warning
warning: binding reference member '_a' to a temporary value [-Wdangling-field]
A() : _a( 0 ) {}
but this code, where std::move is used to initialize the member _a, does not:
struct A
{
A() : _a( std::move(0) ) {}
const int& _a;
};
int main()
{
A a;
}
Aren't 0 and std::move( 0 ) both r-values?
This is an expression:
0
It's a very small expression, true. But it is an expression.
Once the expression is evaluated, it goes away. It disappears. Joins the choir invisible. Goes to meet its maker. It becomes an ex-expression.
It is true that binding a const reference to a temporary extends the scope of the temporary value until the end of the enclosing scope.
But in this case, the scope of the expression is the constructor. When the constructor is done, the temporary value gets destroyed.
Your compiler noticed the fact that a const reference to the expression still continues to exist, though, as a class member. Your compiler is advising you that using the class member will now result in undefined behavior. Your compiler wants to be your friend. Your compiler doesn't want you to write buggy code, so you're getting some free, friendly advice, from your compiler.
In the other case, you have added some additional code which is slightly more complicated. It is still undefined behavior, but the code is now complex enough that the compiler cannot see that undefined behavior results. But it's still the same bug.
A compiler will try to warn you of potential problems, when the compiler sees them. Unfortunately, the compiler cannot find all possible potential problems, every time. But, when it's obvious, the compiler will let you know.
Their return values are not exactly the same.
From cppreference.com
In particular, std::move produces an xvalue expression that identifies
its argument t. It is exactly equivalent to a static_cast to an rvalue
reference type.
Now, looking at rvalue references, we see that object "0" in second example can live longer:
An rvalue may be used to initialize an rvalue reference, in which case
the lifetime of the object identified by the rvalue is extended until
the scope of the reference ends.
Such reference (rvalue reference) is afterwards assigned to the class member _a, which is allowed, so you are having no error.
Moreover, rvalue reference to a temporary can be used in a move constructor, so if the member you are initialising has it, I don't see the problem. However, in C++ you can never know when undefined behaviour can suddenly hit you :)
Related
This is a very minimal example:
class Foo
{
public:
Foo(int x) {};
};
void ProcessFoo(Foo& foo)
{
}
int main()
{
ProcessFoo(Foo(42));
return 0;
}
The above compiles fine on Visual Studio, but generates an error on Linux and Mac.
Compiling the above generates this:
$ g++ -std=c++11 -c newfile.cpp
newfile.cpp: In function ‘int main()’:
newfile.cpp:23:23: error: invalid initialization of non-const reference of type ‘Foo&’ from an rvalue of type ‘Foo’
ProcessFoo(Foo(42));
^
newfile.cpp:14:6: note: in passing argument 1 of ‘void ProcessFoo(Foo&)’
void ProcessFoo(Foo& foo)
I've found three workarounds:
Create a temp variable for the invocation of ProcessFoo.
Like this:
Foo foo42(42);
ProcessFoo(foo42);
ProcessFoo takes a const reference: void ProcessFoo(const Foo& foo)
ProcessFoo just lets Foo get passed by value. void ProcessFoo(Foo foo)
Why is the compiler forbidding my original code? (What is it guarding against)? What is it about each of the three workarounds above that satisfies the compiler? What would MSVC allow it, but not g++?
By design, C++ only allows a temporary to be passed to a const reference, value, or rvalue reference. The idea is that a function taking a non-const reference parameter is stating that it wants to modify the parameter and allowing it to go back to the caller. Doing so with a temporary is meaningless and most likely an error.
And I don't know what version of g++ you're running. It doesn't work here: http://coliru.stacked-crooked.com/a/43096cb398cbc973
Why is the compiler forbidding my original code?
Because it is forbidden by the Standard:
8.5.3 References 5
...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const), or the reference shall be an rvalue reference.
[ Example:
double& rd2 = 2.0; // error: not an lvalue and reference not const
...
'
What is it guarding against?
Inadvertently modifying an object that is going to be destructed after the function call.
What is it about each of the three workarounds above that satisfies the compiler?
1 Creates a named object and 3 a copy.
2 Works because the lifetime of the object is simply extended, and changes to it are prevented at the same time.
What would MSVC allow it, but not g++?
Because it is a language extension. Disable it by going to Property Pages->C/C++->Language->Disable Language Extensions and you'll get an error.
Why is the compiler forbidding my original code?
MSVC has an extension that allows temporaries to bind to non-const lvalue-references. Of course this isn't a standard-conforming feature so I would stay away from it to be portable. For example, it doesn't work with the latest versions of GCC and Clang as you've seen.
What is it about each of the three workarounds above that satisfies the compiler?
Back in C++03, expressions could only be lvalues or rvalues. References could only designate the "lvalueness" of an object, and so it was used with the intention of aliasing a preexisting object. By contrast, rvalues don't exist beyond the expression in which they appear. Also, the end result of references was normally to copy or modify the object, and it doesn't make much sense to the language to modify an rvalue like 55 for example.
The rules allow you to bind an rvalue to a lvalue-reference to const, in which case the temporary's lifetime is extended to the lifetime of the reference. When you take an object by value the object is copied.
With C++11 we have rvalue-references and xvalues which were made for the purpose of exchanging ownership. With this is lessens the usefulness of lvalue-references to const. Moreover, taking by-value causes a move if it is an rvalue.
Once you declared the prototype for ProcessFoo as
void ProcessFoo(Foo& foo)
You are conveying your intent as the formal parameter "foo" is subject to modification as it is not being passed by const &.
At the call-site,
ProcessFoo(Foo(42));
Foo(42) is creating a temporary stack object that is not modifiable. It is okay to pass-by-value or pass-by-ref-to-const to a method.
As you listed yourself, satisfying those constraints, makes the compiler happy.
is giving you an object that is not compiler generated and is under your control.
Informs the compiler that the method guarantees const-ness of the object.
Informs the compiler that this (temporary) object is passed by value and hence no issues.
I have the following questions related to the same situation (not in general):
Why does the compiler not produce a warning when a temporary is bound
to a reference?
How does lifetime extension of a temporary work (when it is bound to
a reference)?
How to interpret / understand the C++ standard (C++11)?
Is this a bug (in the compiler)? Should it be?
So this is what we are talking about:
struct TestRefInt {
TestRefInt(const int& a) : a_(a) {};
void DoSomething() { cout << "int:" << a_ << endl; }
protected:
const int& a_;
};
Should TestRefInt tfi(55); and tfi.DoSomething(); work? How and where?
So here is a code
TestRefInt tfi(55);
int main() {
TestRefInt ltfi(8);
tfi.DoSomething();
ltfi.DoSomething();
return 0;
}
What should this do?
Here I will elaborate some more.
Consider this real world (simplified) example. How does it look like to a novice/beginner C++ programmer? Does this make sense?
(you can skip the code the issue is the same as above)
#include <iostream>
using namespace std;
class TestPin //: private NonCopyable
{
public:
constexpr TestPin(int pin) : pin_(pin) {}
inline void Flip() const {
cout << " I'm flipping out man : " << pin_ << endl;
}
protected:
const int pin_;
};
class TestRef {
public:
TestRef(const TestPin& a) : a_(a) {};
void DoSomething() { a_.Flip(); }
protected:
const TestPin& a_;
};
TestRef g_tf(1);
int main() {
TestRef tf(2);
g_tf.DoSomething();
tf.DoSomething();
return 0;
}
Command line:
/** Compile:
Info: Internal Builder is used for build
g++ -std=c++11 -O0 -g3 -Wall -Wextra -Wconversion -c -fmessage-length=0 -o "src\\Scoping.o" "..\\src\\Scoping.cpp"
g++ -o Scoping.exe "src\\Scoping.o"
13:21:39 Build Finished (took 346ms)
*/
Output:
/**
I'm flipping out man : 2293248
I'm flipping out man : 2
*/
The issue:
TestRef g_tf(1); /// shouldn't the reference to temporary extend it's life in global scope too?
What does the standard says?
From How would auto&& extend the life-time of the temporary object?
Normally, a temporary object lasts only until the end of the full
expression in which it appears. However, C++ deliberately specifies
that binding a temporary object to a reference to const on the stack
lengthens the lifetime of the temporary to the lifetime of the
reference itself
^^^^^
Globals are not allocated on stack, so lifetime is not extended. However, the compiler does not produce any warning message whatsoever! Shouldn’t it at least do that?
But my main point is: from a usability standpoint (meaning as the user of C++, GCC as a programmer) it would be useful if the same principle would be extended to not just stack, but to global scope, extending the lifetime (making global temporaries “permanent”).
Sidenote:
The problem is further complicated by the fact that the TestRef g_tf(1); is really TestRef g_tf{ TestPin{1} }; but the temporary object is not visible in the code, and without looking at the constructor declaration it looks like the constructor is called by an integer, and that rarely produces this kind of error.
As far as I know the only way to fix the problem is to disallow temporaries in initialization by deleting TestRefInt(const int&& a) =delete; constructor. But this also disallows it on stack, where lifetime extension worked.
But the previous quote is not exactly what the C++11 standard say. The relevant parts of the C++11 Standard are 12.2 p4 and p5:
4 - There are two contexts in which temporaries are destroyed at a
different point than the end of the full expression. The first context
is [...]
5 - The second context is when a reference is bound to a
temporary. The temporary to which the reference is bound or the
temporary that is the complete object of a subobject to which the
reference is bound persists for the lifetime of the reference except:
— A temporary bound to a reference member in a constructor’s
ctor-initializer (12.6.2) persists until the constructor exits.
— 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. § 12.2 245 c ISO/IEC N3337
— The lifetime of a temporary bound
to the returned value in a function return statement (6.6.3) is not
extended; the temporary is destroyed at the end of the full-expression
in the return statement.
— A temporary bound to a reference in a
new-initializer (5.3.4) persists until the completion of the
full-expression containing the new-initializer. [Example: struct S {
int mi; const std::pair& mp; }; S a { 1, {2,3} }; S* p = new S{ 1,
{2,3} }; // Creates dangling reference — end example ] [ Note: This
may introduce a dangling reference, and implementations are encouraged
to issue a warning in such a case. — end note ]
My English and understanding of the standard is not good enough, so what exception is this?
The “— A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.” or “reference parameter in a function call” does not mention anything about allocation on stack or in global scope. The other exceptions do not seem to apply. Am I wrong?
Which one is this, or none of them?
We have a temporary in a function call (the constructor) and then a reference to
the temporary is bound to a member reference in the initializer. Is this undefined behavior? Or the exception still applies, and the temporary should be destroyed? (or both)
What about this?
struct TestRefIntDirect {
TestRefIntDirect(int a) : a_(a) {};
void DoSomething() { cout << "int:" << a_ << endl; }
protected:
const int& a_;
};
One less reference, same behavior.
Why does it work in one case (instantiation inside a function) versus in other case (in global scope)?
Does GCC not “destroy” one of them by “accident”?
My understanding is that none of them should work, the temporary should not persist as the standard says. It seems GCC just "lets" you access not persisting objects (sometimes). I guess that the standard does not specify what the compiler should warn about, but can we agree that it should? (in other cases it does warn about ‘returning reference to temporary’) I think it should here too.
Is this a bug or maybe there should be a feature request somewhere?
It seems to me like GCC says “Well, you shouldn’t touch this cookie, but I will leave it here for you and not warn anyone about it.” ...which I don’t like.
This thing others reference about stack I did not find in the standard, where is it coming from? Is this misinformation about the standard? Or is this a consequence of something in the standard? (Or is this just implementation coincidence, that the temporary is not overwritten and can be referenced, because it happened to be on the stack? Or is this compiler defined behavior, extension?)
The more I know C++ the more it seems that every day it is handing me a gun to shoot myself in the foot with it…
I would like to be warned by the compiler if I’m doing something wrong, so I can fix it. Is that too much to ask?
Other related posts:
Temporary lifetime extension
C++: constant reference to temporary
Does a const reference prolong the life of a temporary?
Returning temporary object and binding to const reference
I didn’t want to write this much. If you read it all, thanks.
This is a follow-up of these questions.
Consider the following code:
struct A {
private:
A* const& this_ref{this};
};
int main() {
A a{};
(void)a;
}
If compiled with the -Wextra, both GCC v6.2 and clang v3.9 show a warning.
Anyway, with the slightly modified version shown below they behave differently:
struct A {
A* const& this_ref{this};
};
int main() {
A a{};
(void)a;
}
In this case GCC doesn't give any warning, clang gives the same warning as returned in the previous example.
The warnings are almost the identical.
It follows the one from clang:
3 : warning: binding reference member 'this_ref' to a temporary value [-Wdangling-field]
Which compiler is right?
I would say that GCC is wrong in this case and I were opening an issue, but maybe it's the opposite because of an arcane corner case of the language.
The member declaration
A* const& this_ref{this};
binds a reference to a temporary that only exists during constructor execution (note: this is an rvalue expression).
I'm not sure if this is formally available in that context, but if it is then with any use of that pointer you have serious case of UB.
Re
” Which compiler is right?
… a compiler can issue as many diagnostics as it wants. It's not wrong to issue a diagnostic. So per your description, that both accept the code, then either both compilers are right (which I think is most likely), or both are wrong.
The reason for this warning is IMO this excerpt from standard (12.2.5):
A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the
constructor exits.
and since the keyword this is a prvalue expression, during this_ref initialization a temporary will be created and this_ref is bound to that temporary.
But I have doubt whether your reference is actually initialized in ctor-initializer.
If you write:
struct A {
private:
const int& rr = 1+1;
};
then you will reproduce the exact same problem with gcc, removing private will also remove this warning.
From what I know this pointer might be used in the body of the non-static member function, I have never read that it could be used as argument during default member initialization.
this is prvalue, and temporary object will be created when binding reference to a prvalue, so you're binding reference member to a temporary in default member initializer.
And binding reference member to temporary in default member initializer is ill-formed, which is stated by the standard explicitly.
$12.6.2/11 Initializing bases and members
[class.base.init]:
A temporary expression bound to a reference member from a default
member initializer is ill-formed. [ Example:
struct A {
A() = default; // OK
A(int v) : v(v) { } // OK
const int& v = 42; // OK
};
A a1; // error: ill-formed binding of temporary to reference
A a2(1); // OK, unfortunately
— end example ]
And see CWG 1696, this is applied to C++14.
Consider the following:
struct my_type {};
my_type make_my_type() { return my_type{}; }
void func(my_type&& arg) {}
int main()
{
my_type&& ref = make_my_type();
func(ref);
}
Needless to say, this code doesn't compile. I realise that I need to use std::move() in the second function call, but for the purposes of understanding I want to consider the code as it is.
Attempting to compile the above, Clang 3.5 tells me:
error: no matching function for call to 'func'
note: candidate function not viable: no known conversion from 'my_type' to 'my_type &&' for 1st argument void func(my_type&&) {}
While g++ 4.9 says something almost identical:
error: cannot bind 'my_type' lvalue to 'my_type&&'
note: initializing argument 1 of 'void func(my_type&&)'
These error messages have me rather confused, because while ref is certainly an lvalue, its type is still my_type&&... isn't it?
I'm trying to understand exactly what's going on here, so I'm wondering which (if any) of the following are true:
Since only rvalues can be bound to rvalue references, and ref is an lvalue, it cannot be bound to arg. The error messages from both Clang and g++ are misleading in claiming that ref is a (non-reference) my_type that "cannot be converted".
Because it is an lvalue, ref is treated for the purposes of overload resolution as a non-reference my_type, despite its actual type being my_type&&. The error messages from Clang and g++ are misleading because they are displaying the type as used internally for function matching, not the real type of ref.
In the body of main(), the type of ref is plain my_type, despite the fact I explicitly wrote my_type&&. So the error messages from the compilers are accurate, and it is my expectation that is wrong. This doesn't seem to be the case however, since
static_assert(std::is_same<decltype(ref), my_type&&>::value, "");
passes.
There is some other magic going on that I haven't considered.
Just to repeat, I know that the solution is to use std::move() to convert the rref back into an rvalue; I'm looking for an explanation of what's going on "behind the scenes".
Consider these three assignments:
my_type x = func_returning_my_type_byvalue();
my_type & y = func_returning_my_type_byvalue();
my_type && z = func_returning_my_type_byvalue();
The first - you have a local variable x and it's being initialized to the result of a function call (rvalue) so a move constructor/assignment can be used or the construction of x could be elided entirely (skipped over and x is constructed in-place by func_returning_my_type_byvalue when it generates its result).
Note that x is an lvalue - you can take its address, so therefore it is also a type of reference itself. Technically all variables that are not references, are references to themselves. In that respect lvalues are a binding site for assignments to and reads from known-storage-duration memory.
The second will not compile - you cannot assign a reference to a result (this way), you must use reference assignment syntax to alias an existing lvalue. It is perfectly fine to do this, however:
my_type & y = func_returning_my_type_byreference();
// `y` will never use constructors or destructors
This is why the third exists, when we need a reference to something we cannot create a reference to using the conventional syntax. Within something like func in the original question, the lifetime of arg is not immediately obvious. For example we can't do this without an explicit move:
void func( my_type && arg ) {
my_type && save_arg = arg;
}
The reason this is not allowed is because arg is a reference to a value first and foremost. If the storage of arg's value (what it's referring to) were to be shorter than that of save_arg, then save_arg would call the destructor of that value - in effect capturing it. That is not the case here, save_arg will disappear first, so it makes no sense to transfer an lvalue into it that we can, after func, still refer to potentially!
Consider that even if you were to use std:move to force this to compile. The destructor will still not be called within func because you haven't created a new object, just a new reference, and then this reference is destroyed before the original object itself went out of scope.
For all intents and purposes arg behaves as if it's my_type&, as do any rvalue references. The trick is storage duration and the semantics of lifetime extension by reference passing. It's all regular references under the hood, there is no 'rvalue type'.
If it helps, recall the increment / decrement operators. There are two overloads that exist, not two operators. operator++(void) (pre) and operator++(int) (post). There is never an actual int being passed, it's just so the compiler has different signatures for different situations / contexts / agreements about value treatment. This is sort of the same deal with references.
If rvalue and lvalue references are both always referred to like an lvalue, what's the difference?
In a word: object lifetime.
An lvalue reference must always be assigned to using something with longer storage duration, something that is already constructed. That way there is no need to call constructors or destructors for the scope of the lvalue reference variable, because by definition we are given a ready object and forget about it before it's due to be destroyed.
it's also relevant that objects are implicitly destroyed in the reverse order they're defined:
int a; // created first, destroyed last
int b; // created second, destroyed 2nd-last
int & c = b; // fine, `c` goes out of scope before `b` per above
int && d = std::move(a); // fine, `a` outlives `d`, same situation as `c`
If we assigned to an rvalue reference, something that is an lvalue reference, the same rule applies - the lvalue must by definition have longer storage, so we don't need to call constructors or destructors for c or even d. You can't trick the compiler with std::move on this because it knows the scope of the object being moved - d is unambiguously shorter-duration than the reference it's being given, we're just forcing the compiler to use the rvalue type check / context and that's all we've achieved.
The difference is with non-lvalue references - things like expressions where there can be references to them but these references are definitely short-lived, perhaps shorter than the duration of a local variable. Hint Hint.
When we assign the result of a function call or an expression to an rvalue reference, we are creating a reference to a temporary object that otherwise could not be referred to. Due to this, we are in effect forcing in-place construction of a variable from the result of an expression. This is a variation on copy/move elision where the compiler has no choice but to elide the temporary to in-place construction:
int a = 2, b = 3; // lvalues
int && temp = a + b; // temp is constructed in-place using the result of operator+(int,int)
The case with func
It boils down to an lvalue assignment - references as function arguments refer to objects that may exist for longer than a function call, and as such are lvalues even when the argument type is an rvalue reference.
The two cases are:
func( std::move( variable ) ); // case 1
func( my_type() + my_type() ); // case 2
func is not allowed to guess which situation we will use it in ahead of time (sans optimizations). If we didn't allow case 1, then there would be a legitimate reason to consider an rvalue reference parameter as having less storage duration than the function call, but that would also make no sense because either the object is always cleaned up inside func or always outside of it, and having "unknown" storage duration at compile time is not satisfactory.
The compiler has no choice but to assume the worst, that case 1 might happen eventually, in which case we must make guarantees to the storage duration of arg as being longer than the call to func in the general case. As consequence of this - that arg would be considered to exist for longer than the call to func some of the time, and that func's generated code must work in both cases - arg's allowable usage and assumed storage duration meet the requirements of my_type& and not my_type&&.
Take a look at the following code:
struct s
{
s& operator+() {return*this;}
};
void foo(s &) {}
s bar() {}
int main()
{
foo(bar()); //comp error
foo(+bar()); //ok
}
I think the two lines in main() should be equivalent, because the compiler optimizes away operator+(), right? What sense does it make to accept one but not the other?
The line foo(bar()) is trying to bind an rvalue of type s (the temporary returned by bar()) to a non-const lvalue reference to s (the argument of foo()). This is illegal, hence the compilation error. rvalues can only be bound to rvalue references or to const lvalue references.
The expression +bar(), on the other hand, returns an lvalue reference (that's the return type of operator + ()), which can be bound to the lvalue reference parameter of foo().
Beware though: you are returning an lvalue reference to a temporary here. While using it inside foo() is still safe (the temporary will be destroyed when the full-expression in which it is created is completely evaluated, so after foo() returns), you would get Undefined Behavior if you stored that reference somewhere and dereferenced it later.
Therefore, instead of regarding this as a "solution to a compilation error", you should consider it a way to blindfold the compiler so you are free to sneak into big troubles.