In this example here at the bottom, there are exemplary l-values defined:
// lvalues:
int& foo();
foo() = 42; // ok, foo() is an lvalue
int* p1 = &foo(); // ok, foo() is an lvalue
I am not shure what foo() is here? At first sight it looks like a function/method?
Is int& foo() the same as int& foo; ?
But on the other hand, my compiler says
Error: 'foo' declared as reference but not initialized int & foo;
Same with the rvalues foobar():
// rvalues:
int foobar();
int j = 0;
j = foobar(); // ok, foobar() is an rvalue
int* p2 = &foobar(); // error, cannot take the address of an rvalue
Yes, it is "a function/method", returning a reference to an int. Or, more precisely, this is a declaration of such a function, not definition: it says that such function exists, but does not provide the actual code (because the code is not relevant for the example). Compare how you define functions in header files.
A possible example of code for a similar function:
int a, b;
int& foo(bool which) {
if (which) return a;
else return b;
}
...
foo(true) = 10; // works as a = 10;
Yes, foo is indeed a function. The example is just trying to say "if a function returns an lvalue reference, then the result of calling it is an lvalue."
They probably just mean to say "there is a function foo returning an int& defined." Although technically, the code is correct as-is: it is possible in C++ to declare functions locally:
int main()
{
int foo();
return foo();
}
int foo()
{
return 42;
}
The example is referring to the value category of the function call expression foo(). In this case, foo() is an lvalue because foo returns an int&.
Related
According to §7.1.5.1/4:
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
So my question becomes: when is an object a const object?
In particular, is a const member in a non-const object considered a const object?
class Foo {
const Bar bar;
void replaceBar(Bar bar2) {
*(const_cast<Bar *>&bar) = bar2; // Undefined behavior?
}
}
This comes up because I have an immutable class (all fields are const), but I want to have a move constructor, which technically modifies the value passed in. I'm ok with "cheating" in that case, since it doesn't break logical constness.
The simple rule is: it is ok to cast away constness if the original object is not const. So if you have a non-cont object and, say, you pass the const reference to it to a function, it is legal to cast away constness in the function.
In your example the original object is const, so casting constness away is undefined behaviour.
Let us make this a full example:
struct Bar { int x; };
struct Foo {
const Bar bar;
Foo( int x ):bar(x) {}
void replaceBar(Bar bar2) {
*(const_cast<Bar *>&bar) = bar2; // Undefined behavior?
}
};
now, let us break the world.
int main() {
Foo f(3);
Bar b = {2};
f.replaceBar(b);
std::cout << f.bar.x << "\n";
}
the above can and probably should output 3, because a const object Bar was created with x=3. The compiler can, and should, assume that the const object will be unchanged throughout its lifetime.
Let's break the world more:
struct Bar {
int* x;
Bar(int * p):x(p) {}
~Bar(){ if (x) delete x; }
Bar(Bar&& o):x(o.x){o.x=nullptr;}
Bar& operator=(Bar&& o){
if (x) delete x;
x = o.x;
o.x = nullptr;
}
Bar(Bar const&)=delete;
Bar& operator=(Bar const&)=delete;
};
struct Foo {
const Bar bar;
Foo( int* x ):bar(x) {}
void replaceBar(Bar bar2) {
*(const_cast<Bar *>&bar) = bar2; // Undefined behavior?
}
};
now the same game can result in the compiler deleting something twice.
int main() {
int* p1 = new int(3);
Foo f( p1 );
Bar b( new int(2) );
f.replaceBar(std::move(b));
}
and the compiler will delete p1 once within replaceBar, and should delete it also at the end of main. It can do this, because you guaranteed that f.bar.x would remain unchanged (const) until the end of its scope, then you violated that promise in replaceBar.
Now, this is just things the compiler has reason to do: the compiler can literally do anything once you have modified an object that was declared const, as you have invoked undefined behavior. Nasal demons, time travel -- anything is up for grabs.
Compilers use the fact that some behavior is undefined (aka, not allowed) to optimize.
How can one invoke the second (overloaded) function?
(This example is present in Savitch's C++ textbook.)
(1) int& f(); // will be used in any l-value invocation
(2) const int& f() const; // will be used in any r-value invocation
I thought the first one is invoked in (a) and the second one in (b). But it is not.
(a) f() = 123; // the first one is invoked.
(b) f() + 3; // the first one is also invoked.
Only member functions can be const. So let's assume f actually is a member function in the textbook.
When the compiler has a choice between a const and a non const member function it will only use the const one if it has to. This is when the object the function is called on is const.
class A {
public:
int &f();
const int& f() const;
};
void func()
{
A a;
a.f(); // calls non const version
const A ca;
ca.f(); // call const version
}
(1) int& f(); // will be used in any l-value invocation
(2) const int& f() const; // will be used in any r-value invocation
These comments are wrong. If they appear in a textbook then I would recommend getting a different textbook.
Version 2 will be used when the expression denoting the object has const type, otherwise version 1 is used. It is nothing at all to do with lvalues and rvalues. Using Eelke's class definition:
A().f(); // invokes (1) on an rvalue
A const a;
a.f(); // invokes (2) on an lvalue
In your examples you don't actually show whether you are working on a const instance or not, but judging by your results, both must have been on a non-const one.
Overloading works on function parameters not by return type.
double f();
int f();
These are not overloading.
But these are:
double f();
int f(int);
Lets say I have a class C, and a function make_c(x) which creates instances of C.
C stores x by reference.
How can I write make_c(x) to give a compile error when x is an unnamed temporary (that would of course destruct at the end of line, leaving a dangling reference) but accept named temporaries and other values?
I believe this should have the semantics you're looking for:
template<typename X>
C make_c(X&& x)
{
static_assert(
!std::is_rvalue_reference<decltype(std::forward<X>(x))>::value,
"x must not be a temporary"
);
return C(std::forward<X>(x));
}
Caveat: this won't work as-is with VC++ 2010 due to deficiencies in its implementation of decltype (you'd need to wrap decltype in std::identity<>).
I don't think it's possible within the language, because you'd need to check the flow control through arbitrary functions.
struct Foo{
};
Foo const & sanitize(Foo const & f){ return f;}
void checkThisFunction(Foo const & f){
//we'd like to ensure at compile time that f is not a temporary
}
int main(){
Foo f;
checkThisFunction(sanitize(f));
checkThisFunction(sanitize(Foo()));
return 0;
}
Unless I'm completely misunderstanding rvalue references, this sort of thing should be doable with simple overloading.
void foo(int&&) = delete;
void foo(const int&) { }
int main()
{
int a;
foo(a);
foo(42); //error, prefers binding to the deleted overload
}
I've been reading some OSS code lately and stumbled upon this peculiar piece:
class Foo { ..... };
void bar() {
Foo x;
Foo *y=new Foo();
x=(const Foo &) *y;
}
For the life of me I can't find documentation about the behavior of casting an object to a const reference.
x=(const Foo &) *y; is assignment. The only reason I see to explicitly cast to const reference is to have Foo::operator=(const Foo &) called for assignment if Foo::operator=(Foo &) also exists.
x=(const Foo &) y; line invokes undefined behavior.
Prefer to avoid C-style casts; they are easy to get wrong. You silence the compiler, so they are too dangerous.
Edit: this answer was relevant at the time, when in the question y was not dereferenced prior to the cast to const Foo &. For the answer to the question after *y edit, please see the answer given by n0rd
Interestingly, the misread code could still be possible, if Foo has a non-explicit constructor that takes a Foo* pointer.
#include <iostream>
class Foo{
public:
Foo() {}
Foo(Foo*) { std::cout << "Aha!\n"; }
};
int main(){
Foo* pf = new Foo;
Foo f = (const Foo&)pf;
std::cin.get();
}
See the output at Ideone.
Interestingly, if you make the constructor explicit, it shows the undefined behaviour explained by #usta.
You'll have to declare x after y:
Foo* y = new Foo();
Foo& x = *y;
Alternatively:
Foo x;
Foo* y = new Foo();
x = (Foo&)*y;
If I have the following:
class A {
int foo() const {
j++;
return i;
}
int& foo() {
return i;
}
int i;
mutable int j;
};
then obviously something like
A a;
a.foo() = 5;
calls the non-const version. But what conditions need to be met to make sure that a call is to the const or non-const version for a few examples...
int i = a.foo(); //I would expect to call the const. Is it guaranteed?
const int j = a.foo(); //Ditto
const int& l = a.foo(); //Ditto
int& k = a.foo(); //I would expect the non-const
foobar(k); //foobar is "void foobar(int)"
return; //but the compiler could potentially decide the const version is fine.
const function gets called when the object itself is const.
int i = static_cast<const A>(a).foo(); //calls const function
Also see this code for better understanding: (must read the comments)
void f(const A &a) //a is const inside the function
{
int i = a.foo(); // calls const function
}
void h(A &a) //a is non-const inside the function
{
int i = a.foo(); // calls non-const function
}
A a;
f(a);
h(a); //pass the same object!
See the online demo : http://ideone.com/96flE
The constness of a decides which function - what you do with the return value is not a part of overload resolution. All your samples would call the non-const version.
Return values are never considered when determining which overload to take.
Also, when a is declared as
A a;
then the non const version takes precedence.
If a is declared as
const A a;
then the const version can be called only.
Whether your member function call resolves to a const member function or not, depends on the constness of the "this" pointer i.e. the object on the LHS of dot or arrow operator that is implicitly passed to the called member function.
Resolves to non const:
A a;
a.foo();
Resolves to const:
void bar(const A& a)
{
a.foo();
}