In C++, I've always known the intialization of Foo& f = _f; to create an alias to the member _f. So, from that point on I can call f and it's just like I was calling _f.
I have tried doing this where I am setting an alias to the return of a getter function, and it doesn't seem to be working. How come? This is what I'm trying..
Foo &f = c->getFoo(); //where I have a pointer c that points to an object of type Foo.
Without seeing the declaration, I assume that the problem you have is that you can't get a non const reference for a temporary. So
Foo const&f = c->getFoo();
but
Foo f(c->getFoo());
would probably work as well.
doing Foo &f = c->getFoo() is not an alias. It actually stores the returning address of getFoo() in a reference (Gives you the same object as getFoo() is returning).
It depends on the return type of getFoo(). For example, if your getFoo() returns a Foo& it would work. If your getFoo() returns a const Foo&, then you'll have to do Foo const &f = getFoo().
However, in both cases, you have to make sure that the object returned by getFoo is available in the class c scope, not only in the getFoo() scope (Check that it's not a temporary).
Related
My code is a bit more complicated, but I think the structure can be boiled down to this, imagine the following two classes:
class Foo {
std::list<std::shared_ptr<SomeType>> listOfType;
const std::list<std::shared_ptr<SomeType>>& getList() const {
return listOfType;
}
}
class A {
std::shared_ptr<Foo> foo;
Foo getFoo() {
return (*foo);
}
}
Now consider these three options, after the A class has filled the Foo's list:
A a;
// Do something with a, fills the list inside the foo shared pointer
// Option 1, save the copy of foo, then get the const reference of the list
Foo f = a.getFoo();
const std::list<std::shared_ptr<SomeType>>& fList = f.getList();
// Option 2, get a copy of the list directly from the returned copy of foo
std::list<std::shared_ptr<SomeType>> fList = a.getFoo().getList();
// Option 3, get the const list reference directly from the returned copy of foo
const std::list<std::shared_ptr<SomeType>>& fList = a.getFoo().getList();
Option 3 returns an empty list, the other two options return the list with the expected content.
The reason I'm creating this question is to know whether there is a problem that I don't see with this code (considering all the references and shared pointers, etc), otherwise it will be a problem with code, which would be beyond the scope of the question.
Thanks
Foo getFoo() {
return *foo;
}
In this member function, you are returning a temporary which is a prvalue in the calling expression. Because you call .getList() on it, it will get materialized and become a xvalue (expiring value) and as soon as the expression finishes, it will be destroyed and as you are capturing the reference of the returned list from a temporary object in the third 'option', it will become a dangling reference.
Foo & getFoo() {
return *foo;
}
However if you return a reference, it will be treated as a lvalue in the calling expression so the list won't be a dangling reference.
Have a look at the following code:
struct Foo;
Foo* bar;
struct Foo {
void func() const {
bar = this;
}
}
int main() {
Foo().func();
}
This does not work as bar won't accept a const Foo*. To get around this, const_cast could be used:
struct Foo {
void func() const {
bar = const_cast<Foo*>(this);
}
}
Is this safe? I'm very cautious when it comes to using const_cast, but in this case it seems legit to me.
No, this is potentially dangerous.
func() is marked const which means that it can be called by a const object:
const Foo foo;
foo.func();
Because this is const Foo*.
If you const_cast away the const you end up with a Foo* to a const object. This means that any modification to that object through the non-const pointer (or through any copy of that pointer, bar in this case) will get you undefined behavior since you are not allowed to modify a const object (duh).
Plus the obvious problem is that you're lying to the consumer of class Foo by saying func() won't modify anything while you're doing the opposite.
const_cast is almost never correct and this seems like an XY-problem to me.
If by "safe" you mean "not undefined behavior" then yes, it is safe. The value of bar will point to the created object as you'd expect.
However, const_cast is generally not recommended because it breaks the convention that a const thing will not be changed, and can easily produce undefined behavior (see this comment below). In this case you can simply do:
struct Foo {
void func() const {
bar = const_cast<Foo*>(this);
bar->modify();
}
void modify() { ... }
}
And you will be modifying the object in a const method, which is unexpected in general and undefined behavior if the instance of Foo on which the method is called was initially declared as const. However, it is up to you to get the logic right. Just note that everyone else will expect const things to be const, including the standard library, and usually (if not always) a better code design is possible.
Under some circumstances it is safe, namely when you have a non-const Foo object. If you have a const Foo object however, you're not allowed to modify it, and the compiler will not catch the bug because of the cast. So it is not a good idea to use this.
Note that in your example Foo is a temporary which gets destroyed on the next line of main().
This code does not compile:
class C {};
void foo (C& c) {}
C bar() { return C(); }
int main()
{
foo(bar());
}
Compilation error (GCC 4.1.2) in line foo(bar()):
invalid initialization of non-const reference of type 'C&'
from a temporary of type 'C'
As bar() returns a mutable object, it should compile...
Why C++ does not allow this above code?
EDIT: I have summarize in an answer below all good ideas from all answers ;-)
The applicable rule here is that you can't create a non-const reference to a temporary object. If foo was declared as foo(const C&) the code would be okay.
The temporary object itself is not const, though; you can call non-const member functions on it, e.g., bar().non_const_member_function().
With C++11, foo can be written to take an rvalue reference; in that case, the call would be okay:
void foo(C&&);
foo(bar()); // okay
It's because the value returned by bar is a temporary value. As it's existence is temporary, you can't use a pointer or reference to that.
However, if you store a copy of that temporary, as in your second change, you no longer pass a reference to a temporary object to foo, but a reference to a real tangible object. And in the first case, when you change to a reference to a constant object, the compiler makes sure the temporary object stays around long enough (as per the C++ specification).
The issue is not with the declaration of bar but with that of foo. foo takes a non-const reference, and temporaries can only bind to const references (which then extends the lifetime of the temporary to match that of the reference it is bound to).
Allowing a non-const reference to bind to a temporary doesn't make much sense. A non-const reference implies that it will modify whatever object is bound to it. Modifying a temporary serves no purpose since its lifetime is limited and the changes will be lost as soon as it goes out of scope.
Modifiable (lvalue-)references do not bind to temporary values. However, const-references do bind to temporary values. It has nothing to do with whether the object returned by value is const or not; it's simply a matter of whether the expression is temporary or not.
For example, the following is valid:
struct C { void i_am_non_const() {} };
int main()
{
bar().i_am_non_const();
}
It is a design choice. There is nothing inherently impossible here. Just a design choice.
In C++11, you have a third alternative which is also superior alternative:
void foo(C && c) {}
That is, use rvalue-references.
It's not const, but it is a temporary rvalue. As such, it can't bind to a non-const lvalue reference.
It can bind to a const or rvalue reference, and you can call member functions (const or not) on it:
class C { void f(); };
void foo_const(C const &);
void foo_rvalue(C &&);
foo_const( bar() ); // OK
foo_rvalue( bar() ); // OK
bar().f(); // OK
The real, hard truth is that it makes no sense to get a reference to a temporary value.
The big point of passing an object by reference is that it allows you to modify its state. However, in the case of a temporary, by its very nature, it would not be particularly helpful to be able to modify it, since you have no way of getting another reference to it later in your code to see the changes.
However, this is somewhat different in the case you have a const reference. Since you'll only ever read from a const reference, it makes total sense to be able to use temporaries there. This is why the compiler will "hack" around it for you, and give a more permanent address to temporaries that you want to "turn" into const references.
So, the rule is that you cannot get a non-const reference to a temporary value. (This slightly changed with C++11, where we have a new type of references that serve this exact purpose, but methods are expected to deal with those in a special way.)
Thank you all for your answers :-)
Here I gather your good ideas ;-)
Answer
Return by value is not const. For example, we can call non-const member functions of return by value:
class C {
public:
int x;
void set (int n) { x = n; } // non-const function
};
C bar() { return C(); }
int main ()
{
bar.set(5); // OK
}
But C++ does not allow non-const references to temporary objects.
However C++11 allow non-const rvalue-references to temporary objects. ;-)
Explanation
class C {};
void foo (C& c) {}
C bar() { return C(); }
//bar() returns a temporary object
//temporary objects cannot be non-const referenced
int main()
{
//foo() wants a mutable reference (i.e. non-const)
foo( bar() ); // => compilation error
}
Three fixes
Change foo declaration
void foo (const C& c) {}
Use another object
int main()
{
C c;
foo( c = bar() );
}
Use C++11 rvalue-reference
void foo(C && c) {}
Moreover
To confirm temporary objects are const, this above source code fails for the same reason:
class C {};
void foo(C& c) {}
int main()
{
foo( C() );
}
I've condensed this problem to a small representative sample:
import std.stdio;
class Foo
{
private int f;
}
class State
{
private Foo foo;
const Foo getFoo()
{
return foo; // This line here.
}
}
void main()
{
auto s = new State;
writeln(s.getFoo());
}
I put that code in test.d.
$ gdmd test.d
test.d:13: Error: cannot implicitly convert expression (this.foo) of type const(Foo) to test.Foo
I understand that it's telling me to cast the return value with cast(test.Foo)foo, but why? Why does it interpret the member to be of type const(Foo) and why does it need me to cast away that const? I get the feeling that I'm doing something horribly wrong here.
const Foo getFoo()
is the same as
Foo getFoo() const
It makes the invisible this parameter const. Because const is transitive in D, that means that when you try and return a member variable from this, it's going to be const as well. If foo were a value type, then it would just copy it, and then returning a mutable Foo wouldn't be a problem, because it wouldn't affect the original. But Foo is a class, and therefore is a reference type. So, returning foo is returning a reference to the exact same object that State holds. No copy is made. And so it must be const - otherwise you would be violating the constness of the this parameter.
And no, casting away const is not a good solution. As discussed in this question, casting away const and then mutating the value is effectively illegal in D. You're violating the type system when you do that. The compiler will let you do it, but if you're taking your life into your own hands when you do. It's especially bad if the underlying object is actually immutable, because you can get a segfault in that case. You only cast away const if you absolutely have to, and you never mutate it unless you really know what you're doing.
No, the correct solution is to make the return type const:
const(Foo) getFoo() const
Now, you can return foo and use it. If you want a mutable Foo, then you either have to not have getFoo be const, or you have to have getFoo return a copy of foo. e.g.
Foo getFoo() const
{
//This would be cleaner if you provided a clone/dup function
//of some kind on Foo.
auto retval = new Foo;
retval.f = foo.f;
return retval;
}
The important thing for you to take away from here is that const in D is transitive. As Walter Bright puts it, "it's turtles all the way down." Once something is const, all parts of it are const, and don't cast away const to get around it unless you really know what you're doing. So, if you want to return a reference type which refers to a member variable from a const function, you either need to make the return type const or create a copy of it and return that.
I suspect that what you really want is this:
class State
{
private Foo foo;
// When `this` is const, return const(Foo).
// When `this` is immutable, return immutable(Foo).
// When `this` is mutable, return Foo.
inout(Foo) getFoo() inout
{
return foo;
}
}
Which is equivalent of:
class State
{
private Foo foo;
Foo getFoo()
{
return foo;
}
const(Foo) getFoo() const
{
// `this.foo` is of type const(Foo), as const is transitive.
return foo;
}
immutable(Foo) getFoo() immutable
{
// `this.foo` is of type immutable(Foo), as immutable is transitive.
return foo;
}
}
It interprets the member as const(Foo) because your method signature is const Foo getFoo(), which implies foo will not be mutable.
Remove the const and it should be fine.
class Foo
{
int Bar;
public:
int& GetBar() const
{
return Bar;
}
}
Is it okay that GetBar is a const method? It's not actually changing anything, but it's providing the "outside world" with a means of changing it.
You have a typo in your code, this is what you probably meant:
class Foo
{
int Bar;
public:
int& GetBar() const
{
return Bar; // Removed the ampersand, because a reference is returned, not an address
}
}
And no, this is not legal. When tagging a method with const, not only are you promising you won't touch any of the internal state of the object, you also promise that you will not return anything that can be used to change the state of the object. A non-const reference may be used to modify the value of Bar outside the scope of GetBar(), hence you are implying that you cannot hold the promise.
You must either change the method to being non-const, return a const reference, or make Bar exempt of the promise by marking it as mutable. E.g.: mutable int Bar; The mutable keyword tells the compiler that the logical constness of the object does not depend on the state of Bar. Then you are allowed to do whatever you please with it.
Nope, since you cant do the following assignment:
const int x; int &y = x;
What you can do though is const int x; const int &y = x;
And of course there is no problem in overloading the method and creating both const and non-const variants.
You probably want to return Bar, not &Bar. Anyway, I dropped this code in Comeau:
class Foo
{
int Bar;
public:
int& GetBar() const
{
return &Bar;
}
};
int main(int argc, char** argv)
{
Foo x;
int y = x.GetBar();
y = 5;
return 0;
}
And got the error:
line 9: error: qualifiers dropped in binding reference of type
"int &" to initializer of type "const int"
return Bar;
^
First of all, to return a reference you don't need to use the ampersand operator on the referenced object; it is needed if you want to get a pointer to it. So, I assume you wanted to write return Bar;.
Then, no, you can't do that; in a const method you have a const this pointer (in your case it would be a const Foo *), which means that any reference you can get to its fields1 will be a const reference, since you're accessing them through a "const path".
Thus, if you try to do what you did in that code you'll get a compilation error, since you'd be trying to initialize an int & (the return value of your method) with a const int & (the reference you get from Bar), which obviously is forbidden.
g++ actually says:
testconstref.cpp: In member function ‘int& Foo::GetBar() const’:
testconstref.cpp:9: error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘const int*’
which is what I just said. :)
If, instead, you return a const reference to a class field from a const method you'll have no problem.
Excluding fields marked as mutable, which tells to the compiler that such fields are modifiable even from const methods; this exception has been introduced to allow const methods to change the "real" state of the object in cases when this do not alter its "logical" state; this can be useful to implement lazy evaluation, reference counting, ...
The members of your class are considered const when you are in a const method. So although Bar is int within your class and not const int, in the context of GetBar() const it is "const int". Therefore returning it as a non-const reference or pointer is just as illegal as doing:
const int y = 57;
int& z = y;
This breaks const-correctness even though the 2nd line has not actually changed anything (yet) in y.
Note incidentally that if your class has pointer members the only thing that is const is the pointers themselves and not what they point to. (Often referred to as "shallow" constness)
Thus this would be legal:
class A
{
Foo * foo;
public:
Foo * getFoo() const // legal. does not have to return const Foo *
{
return foo;
}
};
Note that in your original code you would be allowed to return Bar by non-const reference if it were mutable, because those members are not bound by the constness of member functions.
const modifier to a member function doesn't allow to change the object's state with in it's scope. Compiler just checks whether this function is modifying the state of the object or not in its scope. Taking another example -
class foo
{
int num ;
public :
foo( int anum )
{
anum = 10;
}
int getNum()
{
return num;
}
};
foo obj;
int& myNum = obj.getNum() ; // myNum is just an alias to the private class variable num
myNum = 40; // Actually changes the content of the private variable.
So, compiler just checks access specifiers ( i.e., whether this variable is accessible or not in this scope) but not about the private/public/protected variable's memory location if returned to some other variable.