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.
Related
Why does returning the reference to a pointed-to member variable work, but not the other? I know that a const member function should only return const references, but why does that not seem true for pointers?
class MyClass
{
private:
int * a;
int b;
public:
MyClass() { a = new int; }
~MyClass() { delete a; }
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
};
int main(void)
{
MyClass m;
m.geta() = 5; //works????
m.getb() = 7; //doesn't compile
return 0;
}
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
In a const-function, every data member becomes const in such way that it cannot be modified. int becomes const int, int * becomes int * const, and so on.
Since the type of a in your first function becomes int * const, as opposed to const int *, so you can change the data (which is modifiable):
m.geta() = 5; //works, as the data is modifiable
Difference between : const int* and int * const.
const int* means the pointer is non-const, but the data the pointer points to is const.
int * const means the pointer is const, but the data the pointer points to is non-const.
Your second function tries to return const int &, since the type of b become const int. But you've mentioned the actual return type in your code as int &, so this function would not even compile (see this), irrespective of what you do in main(), because the return type doesn't match. Here is the fix:
const int & getb(void) const { return b; }
Now it compiles fine!.
Because a becomes int * const a;. That is, you cannot change the value of a (change what it points at), just as const says. The const-ness of what a points at is a completely different issue.
Please see my answer here for a thorough discussion of const and const member functions.
Nawaz's answer is very good. However, the point about the compiler catching the error need not always hold. The C++ FAQ warns about it here.
The good news is that the compiler will often catch you if you get this wrong. In particular, if you accidentally return a member of your this object by non-const reference [...] the compiler will often detect it and give you a compile-time error [...].
The bad news is that the compiler won’t always catch you: there are some cases where the compiler simply won’t ever give you a compile-time error message.
Translation: you need to think. If that scares you, find another line of work; “think” is not a four-letter word.
I have two classes TestClass and OtherClass, where TestClass has a member variable of type OtherClass named m_otherClass. Note that this is not declared to be const.
In the minimal example provided below; when m_otherClass is a pointer, then everything compiles and runs fine. If I change this to be a non-pointer, then I get compiler errors (the changes are commented out in the minimal example):
"Non-const function 'setOtherMember' is called on const object"
error: passing 'const OtherClass' as 'this' argument discards qualifiers [-fpermissive] m_otherClass.setOtherMember();
#include <iostream>
#include <memory>
class OtherClass {
public:
void setOtherMember() {
m_otherMember = 2;
std::cout << "Other member is now 2" << std::endl;
}
private:
int m_otherMember = 0;
};
class TestClass {
public:
TestClass(): m_otherClass(std::make_unique<OtherClass>())
// TestClass()
{}
void myMethod() const {
m_otherClass->setOtherMember();
// m_otherClass.setOtherMember();
}
private:
std::unique_ptr<OtherClass> m_otherClass;
// OtherClass m_otherClass; // If changing to this I get the error!!
};
int main() {
TestClass testClass;
testClass.myMethod();
return 0;
}
Is this because myMethod() is const (and then promising not to change any member variables), whereas setOtherMember() is non-const and is changing OtherClass's member variable, and then indirectly also the m_otherClass object?
But why does this not fail then when m_otherClass is a pointer?
And why does the compiler error says that passing 'const OtherClass' as 'this'argument fails, when m_otherClass has not been declared to be const?
Const qualified member functions in most cases aren't allowed to change an object's members state. This means, that every member which is not mutable can not be modified in this function body. When dealing with pointers, you just say, that you won't modify a pointer value, not a pointee itself. It is because constness of a pointer is not propagated on its pointee.
In the upcoming standards it will be possible to change this behaviour by using propagate_const.
A simpler example to demonstrate the difference.
struct Foo { int m; };
const Foo f = {10};
f.m = 20; // Not allowed since modifying f.m modifies f.
struct Bar { int* p; };
int i = 10;
int j = 20;
const Bar b = {&i};
b.p = &j; // Not allowed since modifying b.p modifies b.
*(b.p) = j; // Allowed since it does not change b or b.p. It changes the value
// of what b.p points to.
When you put const on a method, all your data members are treated as being const. And that is why you get the error when you have OtherClass as a value, since it turns into a value of const OtherClass.
Now when you use a pointer of OtherClass you get const std::unique_ptr<OtherClass> and the const applies only to the pointer but not to the value it points to.
But why does this not fail then when m_otherClass is a pointer?
Because it is const correct to modify an object pointed by a const pointer, as long as it is a pointer to non-const such as in your case.
And why does the compiler error says that passing 'const OtherClass' as 'this'argument fails, when m_otherClass has not been declared to be const?
myMethod is declared const. Therefore this is a pointer to const. Therefore the lvalue this->m_otherClass is also const regardless of whether the object named by the lvalue is const or not.
What kind of a return type does the following function prototype have?
int const& myfunctionname(int i)
I am not getting the use of the const and & here. If I am returning objects/arrays/ basically non-local variables, should I use a pointer or a reference if I want to return the location of the object?
It means you're returning a reference to a value that cannot be changed.
If you return a reference to a local variable then you're going to have a problem, as it will reference something that no longer exists:
int const &GetValue()
{
int x = 10;
return x; // THIS IS BAD!
}
Also, it doesn't really make sense to return const references to the integral types (int, long, etc) as they take pretty much the same space as the reference in the first place. However if you do then it's not a big deal.
If you're returning non-local data (ie member data) then use references if the data will always exists, and use pointers if the data may not exist, so that NULL indicates the absence of the data. For example:
const std::string &User()const
{
return m_Username;
}
int Age() const
{
return m_Age; // Pointless to use a reference here
}
const Person *Partner()const
{
return m_Parter; // User may not have a partner, so pointer
}
Make the pointer or reference const if you don't want the caller to be able to modify the object returned.
Such function can return a reference to a member variable of the class it belongs to, but the calling function will not be able to change it. For example:
class Foo
{
public:
Foo() {boo=5;}
public:
int const& myfunctionname(int i) {boo += i; return boo;}
private:
int boo;
}
void func(Foo& foo)
{
const int& a = foo.myfunctionname(6);
a += 7; // Compilation error
int b = a+7; // OK
}
Supplemental:
The most common example of returning a constant value by reference, is probably operator++().
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.
struct A {
int &r;
A (int &i) : r(i) {}
void foo () const {
r = 5; // <--- ok
}
};
The compiler doesn't generate any error at r = 5;.
Does it mean that &r is already const-correct being a reference (logical equivalent of int* const) ? [Here is one related question.]
I'm not sure exactly what you mean by "already const-correct", but:
Assigning to r is the same as assigning to whatever thing was passed into the constructor of A. You're not modifying anything in the instance of A when you do this, so the fact that foo is declared const isn't an obstacle. It's very much as if you'd done this:
struct A {
int * r;
A (int * i) : r(i) {}
void foo () const { *r = 5; }
}
The fact that foo is const means that it doesn't modify anything in the A instance it's called on. There's no conflict between that and having it modify other data it was supplied with.
Of course if you happened to arrange for r to be a reference to some member of A then calling foo would modify the instance of A after all. The compiler can't catch all possible ways in which constness of a member function might be violated; when you declare a member function const you're promising that it doesn't engage in any such subterfuge.
Yes, it's the logical equivalent of int* const.
You may want to create and use appropriately qualified accessors in this case to prevent unwanted alterations to the value r references.
I interpret a const member function as implicitly inserting const just to the left of every data member that doesn't already have such a qualifier. That const is already there implicitly for references (int & const r; is illegal syntax). In other words, references are "already const-correct" to use your nomenclature.
It would be nice if the const qualifier on a member function had the affect of inserting const in every possible valid position for every data member (e.g., data member int ** foo; acts like int const * const * const foo; in a const member function), but that isn't what happens, and it isn't what the standard says will happen.