A function type (lvalue) can be converted to a pointer to function (rvalue).
int func();
int (*func_ptr)() = func;
But from (4.1/1)
An lvalue (3.10) of a non-function, non-array type T can be converted
to an rvalue.
Does it mean that a lvalue to rvalue conversion is not done on functions? Also, when an array decays to pointer doesn't it return a rvalue which is a pointer?
Functions are lvalues. A pointer to a function (a data type) can be
either; if you give it a name, it's an lvalue; otherwise, it's not
(roughly speaking). Pointers to functions obey all of the usual lvalue
to rvalue conversion rules. For simple types like the basic types or
pointers, the lvalue to rvalue conversion basically means reading the
variable.
void func(); // Declares func
(*(&func))(); // The expression &func is an rvalue
void (*pf)() = &func; // pf is an lvalue
(*pf)(); // In the expression *pf, pf undergoes an
// lvalue to rvalue conversion
Note that there is an implicit conversion of function to pointer to
function, and that the () operator works on both functions and
pointers to functions, so the last two lines could be written:
void (*pf)() = func;
pf();
As always, the result of the conversion is an rvalue (unless the
conversion is to a reference type). This is also the case when an array
is implicitly converted to a pointer; both arrays and functions can only
exist as lvalues, but they both implicitly convert to a pointer,
which is an rvalue. But that pointer can be used to initialize a
variable of the appropriate pointer type; such variables are lvalues.
Related
I have a bit confusion about this code:
struct A
{
A& bar()&&;
};
A& A::bar()&&
{
std::cout << "A::bar()&&\n";
return *this;
}
int main()
{
A{}.bar();// called by an rvalue
}
So what I understand is that bar can be called only by a modifiable-rvalue. Until this it is OK. But how can bar return a non-constant lvalue reference to that rvalue?
How bar() binds and returns a modifiable lvalue reference to that rvalue object?
The reason is that the this pointer for a class C can be either C* or const C* - not C& * or C&& * (those aren't actual types; you can't declare a C& * ptr). So, even when your method runs for an rvalue instance of class A, you get one of those two (GodBolt). And when you apply the * operator, you get an lvalue, not an rvalue.
This has to do with [expr.unary.op]/1
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer to T”, the type of the result is “T”. [ Note: Indirection through a pointer to an incomplete type (other than cv void) is valid. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a prvalue, see [conv.lval]. — end note ]
emphasis mine
So when you dereference this yo get an lvalue. It doesn't matter if this is pointing to a temporary object or not, you will always get an lvalue. Since *this is an lvalue, you are legally allowed to return an lvalue reference, the program in syntactically correct. Semantically it is not, but that is a lot harder to test for and is often not something that is diagnosed as it requires quite a bit of static analysis.
It would be cool if the language could be updated where * only yields an lvalue when applied to this in a non-rvalue qualified function.
I'm trying to figure out why the following snippet calls the LValue cast operator overload:
#include <iostream>
class Foo
{
public:
Foo(int i = 0) : i(i) {}
operator const int& () const &
{
std::cout << "lvalue\n";
return i;
}
operator int () const &&
{
std::cout << "rvalue\n";
return i;
}
int i = 0;
};
Foo Fool()
{
return Foo(5);
}
int main()
{
const int& i = Fool();
const int j = Fool();
return 0;
}
The current outputs are:
lvalue
rvalue
But from my understanding Fool() returns an rvalue and since const& can bind to rvalues there is no need to construct an lvalue Foo.
Can anyone explain why lvalue is being constructed? I believe this is a dangling lvalue.
Okay, so the thing to note here is that overload resolution only ever considers one conversion function for i. They don't both participate, and so the reference qualifier cannot be used to differentiate them. For the case of binding a reference
[over.match.ref]
Under the conditions specified in [dcl.init.ref], a reference can be
bound directly to the result of applying a conversion function to an
initializer expression. Overload resolution is used to select the
conversion function to be invoked. Assuming that “reference to cv1 T”
is the type of the reference being initialized, and “cv S” is the type
of the initializer expression, with S a class type, the candidate
functions are selected as follows:
The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S
and yield type “lvalue reference to cv2 T2” (when initializing an
lvalue reference or an rvalue reference to function) or “cv2 T2” or
“rvalue reference to cv2 T2” (when initializing an rvalue reference or
an lvalue reference to function), where “cv1 T” is
reference-compatible with “cv2 T2”, are candidate functions. For
direct-initialization, those explicit conversion functions that are
not hidden within S and yield type “lvalue reference to cv2 T2” (when
initializing an lvalue reference or an rvalue reference to function)
or “rvalue reference to cv2 T2” (when initializing an rvalue reference
or an lvalue reference to function), where T2 is the same type as T or
can be converted to type T with a qualification conversion, are also
candidate functions.
According to the text in bold, when initializing i, our only candidate is operator int const&. So overload resolution can either pass here, or fail entirely. But it cannot select operator int, since that one is not even under consideration. It succeeds because a const qualified lvalue reference can bind to the object argument.
On the other hand, for initializing a value
[over.match.conv]
Under the conditions specified in [dcl.init], as part of an
initialization of an object of non-class type, a conversion function
can be invoked to convert an initializer expression of class type to
the type of the object being initialized. Overload resolution is used
to select the conversion function to be invoked. Assuming that “cv1 T”
is the type of the object being initialized, and “cv S” is the type of
the initializer expression, with S a class type, the candidate
functions are selected as follows:
The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S
and yield type T or a type that can be converted to type T via a
standard conversion sequence are candidate functions. For
direct-initialization, those explicit conversion functions that are
not hidden within S and yield type T or a type that can be converted
to type T with a qualification conversion are also candidate
functions. Conversion functions that return a cv-qualified type are
considered to yield the cv-unqualified version of that type for this
process of selecting candidate functions. A call to a conversion
function returning “reference to X” is a glvalue of type X, and such a
conversion function is therefore considered to yield X for this
process of selecting candidate functions.
So when initializing j both conversion functions participate as overloads, and here the reference qualifier makes a difference.
You do get a dangling reference here, and it seems to be due to a dark corner in the language. The bullet in the first quoted paragraph could probably be refined to consider the binding of const lvlaue references better. Since those may bind to temporaries as well, your second conversion operator could ideally be a candidate under better rules.
As known, the function call which return type is an rvlaue to a function is an lvalue.
A function call is an lvalue if the result type is an lvalue reference
type or an rvalue reference to function type, an xvalue if the result
type is an rvalue reference to object type, and a prvalue otherwise.
#include <iostream>
int a(){ return 1; }
int foo(){ return 1; }
int (&&bar())(){ return a; }
int main()
{
bar() = foo; //error: cannot convert 'int()' to 'int()' in assignment
}
What's wrong with that diagnostic message?
Emphasis mine, [expr.ass]/1:
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a
modifiable lvalue as their left operand and return an lvalue referring to the left operand...
[basic.lval]/6:
Functions cannot be modified, but pointers to functions can be modifiable.
So you may have an lvalue referring to a function but it is not a modifiable lvalue, and cannot be used to modify the function.
The diagnostic message... leaves something to be desired. Clang 3.6 says,
error: non-object type 'int ()' is not assignable
which is clearer.
In the below code I call step as a member function and as a global function on a temporary value. The member function is allowed, and works, whereas the global function is disallowed due to invalid initialisation of non-const reference of type ‘kludge&’ from an rvalue of type ‘kludge’.
I'm trying to understand, from a language perspective, why one behaviour is allowed and the other is not. Technically both calls and functions seem like they'd be compiled identically, or at least could be.
#include <iostream>
struct kludge {
int a;
kludge() {
a = 1;
}
kludge & step() {
a++;
std::cout << a << ",";
return *this;
}
};
kludge get() {
kludge t;
return t;
}
kludge & step( kludge & t ) {
t.a++;
std::cout << t.a << ",";
return t;
}
int main() {
get().step();
step( get() );
}
You cannot bind rvalues to non-const lvalue references1. That applies to step(get()) as the parameter of step, which is a non-const lvalue reference, cannot be bound to the prvalue (pure rvalue) get().
However, member functions can per se be called on object arguments of every value category, be it lvalue or rvalue - [over.match.funcs]/4 and /5:
For non-static member functions, the type of the implicit object
parameter is
“lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier
[..]
For non-static member functions declared without a ref-qualifier, an
additional rule applies:
even if the implicit object parameter is
not const-qualified, an rvalue can be bound to the parameter as long
as in all other respects the argument can be converted to the type of
the implicit object parameter. [ Note: The fact that such an
argument is an rvalue does not affect the ranking of implicit
conversion sequences (13.3.3.2). — end note ]
But if you use so-called ref-qualifiers, you can restrict the value categories that are valid for a particular member function. That is, if you write:
kludge & step() & { /* .. */ }
The call get().step() will be ill-formed too.
1)
This is a well-known fact, but here is [dcl.init.ref]/5, heavily shortened:
A reference to type “cv1 T1” is initialized by an expression of
type “cv2 T2” as follows:
If the reference is an lvalue reference and the initializer expression
is an lvalue [..]
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an lvalue of type “cv3 T3,”
[..]
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.
Temporary cannot bind to non-const reference
step( get() );
// ~~~~~ Creates a temporary object (r-value)
// But step( ) excepts a non-const reference
Why is the Visual Studio compiler happy with
void fn(int *&i)
{
;
}
and
void fn(IUnknown *const &p)
{
;
}
but not
void fn(IUnkown *&p)
{
;
}
where calling it looks like
IDXGIFactory *df = nullptr;
// init df
fn(df);
compiler error is
3 IntelliSense: a reference of type "IUnknown *&" (not const-qualified) cannot be initialized with a value of type "IDXGIFactory *" c:\Users\Carl\Documents\Visual Studio 2013\Projects\Project1\Project5\main.cpp 29 10 Project5
The closest thing I've dug up with research is that the compiler will only do one type conversion at a time, but that can't be right because then the const & version should break from doing a type and const conversion; however it is the & version that actually won't compile.
A non-const lvalue reference (like IUnknown*&) can only bind to an lvalue; it cannot bind to an rvalue. A const-qualified lvalue reference (like IUnknown* const&) can bind to an rvalue.
It's easier to consider a simpler case that does not involve pointers or function calls:
int i = 0;
double x = i; // (1) Well-formed
double const& y = i; // (2) Well-formed
double& z = i; // (3) Ill-formed
Here, i is an object of type int. When i is used in an expression, it is an lvalue.
In (1), we initialize the object x (of type double) from i (of type int). The type does not match, but this is okay, because there is an implicit conversion from int to double. The "result" of this conversion is an rvalue expression(*) of type double, which is used to initialize x.
In (2), we initialize the const-qualified reference y (of type double const&) from i. Again, the types do not match, so the implicit conversion is used to convert the int to double. The "result" of this conversion is an rvalue. As noted at the beginning, a const-qualified reference can bind to an rvalue, so y is bound to the "result" of the conversion.
In (3), we attempt to initialize the non-const reference z (of type double&) from i. The types do not match, so a conversion would be required. The conversion cannot be used here, because the "result" of the conversion is an rvalue, and as noted at the beginning, a non-const reference cannot bind to an rvalue.
C++ has special rules to permit const lvalue references to bind to rvalue expressions. You can find out why from other questions here on StackOverflow, like "How come a non-const reference cannot bind to a temporary object?"
Your case is exactly the same as this one: the type of your argument (IDXGIFactory*) is not the same as the type of the parameter (IUnknown* or a reference thereto), so an implicit conversion is required to convert the argument to the parameter type (in this case, it's a conversion from a pointer to a derived class to a pointer to a base class). The "result" of this conversion is an rvalue expression, however, so it cannot bind to the non-const reference IUnknown*&.
(*)It's really a prvalue; I've used the C++98 expression taxonomy in this answer for simplicity. See this question for information about the C++11 value categories.