Reasons for defining non-const 'get' member functions?
I'm working on learning C++ with Stroustrup's (Programming Principles & Practice Using C++) book. In an exercise we define a simple struct:
template<typename T>
struct S {
explicit S(T v):val{v} { };
T& get();
const T& get() const;
void set(T v);
void read_val(T& v);
T& operator=(const T& t); // deep copy assignment
private:
T val;
};
We're then asked to define a const and a non-const member function to get val
.
I was wondering: Is there any case where it makes sense to have non-const get
function that returns val
?
It seems much cleaner to me that we can't change the value in such situations indirectly. What might be use cases where you need a const and a non-const get
function to return a member variable?
c++ const getter member-functions
|
show 4 more comments
I'm working on learning C++ with Stroustrup's (Programming Principles & Practice Using C++) book. In an exercise we define a simple struct:
template<typename T>
struct S {
explicit S(T v):val{v} { };
T& get();
const T& get() const;
void set(T v);
void read_val(T& v);
T& operator=(const T& t); // deep copy assignment
private:
T val;
};
We're then asked to define a const and a non-const member function to get val
.
I was wondering: Is there any case where it makes sense to have non-const get
function that returns val
?
It seems much cleaner to me that we can't change the value in such situations indirectly. What might be use cases where you need a const and a non-const get
function to return a member variable?
c++ const getter member-functions
9
IMO even cleaner would be no getters or setters at all, and instead have the class be about behavior instead. If you have getters and setters for a member variable, you might as well make itpublic
.
– Some programmer dude
Dec 8 at 13:40
1
The primary reason for using a non-const get member function (or any getter and setter anti-pattern) is to avoid doing proper object-oriented programming.
– Eljay
Dec 8 at 14:16
2
@Someprogrammerdude Not really. Maybe right now that field is just a plain value but you know that when you are going to iterate to develop new requirements it will have to be a computed value, and thus having a getter avoids having to change all the code that accesses the field. Also: having a getter you can easily add a breakpoint on it and check in a debugger where is the code that accesses it. If you have just a field you have to put breakpoints in every place that accesses the field which is much more cumbersome to do.
– Bakuriu
Dec 8 at 14:49
A const getter makes sense if the setter had to make some checks (i.e. establish invariants) before setting the value which, in cases when it needs an invariant, is usually made private to allow modification through a constructor and a setter only (which would guarantee the invariants). In general, in my experience setters and getters, and especially non-const getters, are more often a result of oral and written tradition rather than a conclusion of thoughts specific to a particular case. However, notice that Stroustrup's example is a generic case meant to simply introduce the idea.
– Leo Heinsaar
Dec 9 at 11:22
@Bakuriu I guess what Some was refering to is that other classes accessing private fields is bad design in the first place. Just a stupid example: bad design is to have acustomer::getMoney()
andcustomer::setMoney(int)
when it actually should be acustomer::pay(int)
. All your concerns are covered bypay
plus you dont break encapsulation
– user463035818
Dec 13 at 12:21
|
show 4 more comments
I'm working on learning C++ with Stroustrup's (Programming Principles & Practice Using C++) book. In an exercise we define a simple struct:
template<typename T>
struct S {
explicit S(T v):val{v} { };
T& get();
const T& get() const;
void set(T v);
void read_val(T& v);
T& operator=(const T& t); // deep copy assignment
private:
T val;
};
We're then asked to define a const and a non-const member function to get val
.
I was wondering: Is there any case where it makes sense to have non-const get
function that returns val
?
It seems much cleaner to me that we can't change the value in such situations indirectly. What might be use cases where you need a const and a non-const get
function to return a member variable?
c++ const getter member-functions
I'm working on learning C++ with Stroustrup's (Programming Principles & Practice Using C++) book. In an exercise we define a simple struct:
template<typename T>
struct S {
explicit S(T v):val{v} { };
T& get();
const T& get() const;
void set(T v);
void read_val(T& v);
T& operator=(const T& t); // deep copy assignment
private:
T val;
};
We're then asked to define a const and a non-const member function to get val
.
I was wondering: Is there any case where it makes sense to have non-const get
function that returns val
?
It seems much cleaner to me that we can't change the value in such situations indirectly. What might be use cases where you need a const and a non-const get
function to return a member variable?
c++ const getter member-functions
c++ const getter member-functions
asked Dec 8 at 13:35
Juri
16812
16812
9
IMO even cleaner would be no getters or setters at all, and instead have the class be about behavior instead. If you have getters and setters for a member variable, you might as well make itpublic
.
– Some programmer dude
Dec 8 at 13:40
1
The primary reason for using a non-const get member function (or any getter and setter anti-pattern) is to avoid doing proper object-oriented programming.
– Eljay
Dec 8 at 14:16
2
@Someprogrammerdude Not really. Maybe right now that field is just a plain value but you know that when you are going to iterate to develop new requirements it will have to be a computed value, and thus having a getter avoids having to change all the code that accesses the field. Also: having a getter you can easily add a breakpoint on it and check in a debugger where is the code that accesses it. If you have just a field you have to put breakpoints in every place that accesses the field which is much more cumbersome to do.
– Bakuriu
Dec 8 at 14:49
A const getter makes sense if the setter had to make some checks (i.e. establish invariants) before setting the value which, in cases when it needs an invariant, is usually made private to allow modification through a constructor and a setter only (which would guarantee the invariants). In general, in my experience setters and getters, and especially non-const getters, are more often a result of oral and written tradition rather than a conclusion of thoughts specific to a particular case. However, notice that Stroustrup's example is a generic case meant to simply introduce the idea.
– Leo Heinsaar
Dec 9 at 11:22
@Bakuriu I guess what Some was refering to is that other classes accessing private fields is bad design in the first place. Just a stupid example: bad design is to have acustomer::getMoney()
andcustomer::setMoney(int)
when it actually should be acustomer::pay(int)
. All your concerns are covered bypay
plus you dont break encapsulation
– user463035818
Dec 13 at 12:21
|
show 4 more comments
9
IMO even cleaner would be no getters or setters at all, and instead have the class be about behavior instead. If you have getters and setters for a member variable, you might as well make itpublic
.
– Some programmer dude
Dec 8 at 13:40
1
The primary reason for using a non-const get member function (or any getter and setter anti-pattern) is to avoid doing proper object-oriented programming.
– Eljay
Dec 8 at 14:16
2
@Someprogrammerdude Not really. Maybe right now that field is just a plain value but you know that when you are going to iterate to develop new requirements it will have to be a computed value, and thus having a getter avoids having to change all the code that accesses the field. Also: having a getter you can easily add a breakpoint on it and check in a debugger where is the code that accesses it. If you have just a field you have to put breakpoints in every place that accesses the field which is much more cumbersome to do.
– Bakuriu
Dec 8 at 14:49
A const getter makes sense if the setter had to make some checks (i.e. establish invariants) before setting the value which, in cases when it needs an invariant, is usually made private to allow modification through a constructor and a setter only (which would guarantee the invariants). In general, in my experience setters and getters, and especially non-const getters, are more often a result of oral and written tradition rather than a conclusion of thoughts specific to a particular case. However, notice that Stroustrup's example is a generic case meant to simply introduce the idea.
– Leo Heinsaar
Dec 9 at 11:22
@Bakuriu I guess what Some was refering to is that other classes accessing private fields is bad design in the first place. Just a stupid example: bad design is to have acustomer::getMoney()
andcustomer::setMoney(int)
when it actually should be acustomer::pay(int)
. All your concerns are covered bypay
plus you dont break encapsulation
– user463035818
Dec 13 at 12:21
9
9
IMO even cleaner would be no getters or setters at all, and instead have the class be about behavior instead. If you have getters and setters for a member variable, you might as well make it
public
.– Some programmer dude
Dec 8 at 13:40
IMO even cleaner would be no getters or setters at all, and instead have the class be about behavior instead. If you have getters and setters for a member variable, you might as well make it
public
.– Some programmer dude
Dec 8 at 13:40
1
1
The primary reason for using a non-const get member function (or any getter and setter anti-pattern) is to avoid doing proper object-oriented programming.
– Eljay
Dec 8 at 14:16
The primary reason for using a non-const get member function (or any getter and setter anti-pattern) is to avoid doing proper object-oriented programming.
– Eljay
Dec 8 at 14:16
2
2
@Someprogrammerdude Not really. Maybe right now that field is just a plain value but you know that when you are going to iterate to develop new requirements it will have to be a computed value, and thus having a getter avoids having to change all the code that accesses the field. Also: having a getter you can easily add a breakpoint on it and check in a debugger where is the code that accesses it. If you have just a field you have to put breakpoints in every place that accesses the field which is much more cumbersome to do.
– Bakuriu
Dec 8 at 14:49
@Someprogrammerdude Not really. Maybe right now that field is just a plain value but you know that when you are going to iterate to develop new requirements it will have to be a computed value, and thus having a getter avoids having to change all the code that accesses the field. Also: having a getter you can easily add a breakpoint on it and check in a debugger where is the code that accesses it. If you have just a field you have to put breakpoints in every place that accesses the field which is much more cumbersome to do.
– Bakuriu
Dec 8 at 14:49
A const getter makes sense if the setter had to make some checks (i.e. establish invariants) before setting the value which, in cases when it needs an invariant, is usually made private to allow modification through a constructor and a setter only (which would guarantee the invariants). In general, in my experience setters and getters, and especially non-const getters, are more often a result of oral and written tradition rather than a conclusion of thoughts specific to a particular case. However, notice that Stroustrup's example is a generic case meant to simply introduce the idea.
– Leo Heinsaar
Dec 9 at 11:22
A const getter makes sense if the setter had to make some checks (i.e. establish invariants) before setting the value which, in cases when it needs an invariant, is usually made private to allow modification through a constructor and a setter only (which would guarantee the invariants). In general, in my experience setters and getters, and especially non-const getters, are more often a result of oral and written tradition rather than a conclusion of thoughts specific to a particular case. However, notice that Stroustrup's example is a generic case meant to simply introduce the idea.
– Leo Heinsaar
Dec 9 at 11:22
@Bakuriu I guess what Some was refering to is that other classes accessing private fields is bad design in the first place. Just a stupid example: bad design is to have a
customer::getMoney()
and customer::setMoney(int)
when it actually should be a customer::pay(int)
. All your concerns are covered by pay
plus you dont break encapsulation– user463035818
Dec 13 at 12:21
@Bakuriu I guess what Some was refering to is that other classes accessing private fields is bad design in the first place. Just a stupid example: bad design is to have a
customer::getMoney()
and customer::setMoney(int)
when it actually should be a customer::pay(int)
. All your concerns are covered by pay
plus you dont break encapsulation– user463035818
Dec 13 at 12:21
|
show 4 more comments
6 Answers
6
active
oldest
votes
Non-const getters?
Getters and setters are merely convention. Instead of providing a getter and a setter, a sometimes used idiom is to provide something along the line of
struct foo {
int val() const { return val_; }
int& val() { return val_; }
private:
int val_;
};
Such that, depending on the constness of the instance you get a reference or a copy:
void bar(const foo& a, foo& b) {
auto x = a.val(); // calls the const method returning an int
b.val() = x; // calls the non-const method returning an int&
};
Whether this is good style in general is a matter of opinion. There are cases where it causes confusion and other cases where this behaviour is just what you would expect (see below).
In any case, it is more important to design the interface of a class according to what the class is supposed to do and how you want to use it rather than blindly following conventions about setters and getters (eg you should give the method a meaningful name that expresses what it does, not just in terms of "pretend to be encapsulated and now provide me access to all your internals via getters", which is what using getters everywhere actually means).
Concrete example
Consider that element access in containers is usually implemented like this. As a toy example:
struct my_array {
int operator(unsigned i) const { return data[i]; }
int& operator(unsigned i) { return data[i]; }
private:
int data[10];
};
It is not the containers job to hide the elements from the user (even data
could be public). You dont want different methods to access elements depending on whether you want to read or write the element, hence providing a const
and a non-const overload makes perfectly sense in this case.
non-const reference from get vs encapsulation
Maybe not that obvious, but it is a bit controversial whether providing getters and setters supports encapsulation or the opposite. While in general this matter is to a large extend opinion based, for getters that return non const references it is not so much about opinions. They do break encapuslation. Consider
struct broken {
void set(int x) {
counter++;
val = x;
}
int& get() { return x; }
int get() const { return x; }
private:
int counter = 0;
int value = 0;
};
This class is broken as the name suggests. Clients can simply grab a reference and the class has no chance to count the number of times the value is modified (as the set
suggests). Once you return a non-const reference then regarding encapsulation there is little difference to making the member public. Hence, this is used only for cases where such behaviour is natural (eg container).
PS
Note that your example returns a const T&
rather than a value. This is reasonable for template code, where you dont know how expensive a copy is, while for an int
you wont gain much by returning a const int&
instead of an int
. For the sake of clarity I used non-template examples, though for templated code you would probably rather return a const T&
.
add a comment |
First let me rephrase your question:
Why have a non-const getter for a member, rather than just making the member public?
Several possible reasons reasons:
1. Easy to instrument
Who said the non-const getter needs to be just:
T& get() { return val; }
? it could well be something like:
T& get() {
if (check_for_something_bad()) {
throw std::runtime_error{"Attempt to mutate val when bad things have happened");
}
return val;
}
However, as @BenVoigt suggests, it is more appropriate to wait until the caller actually tries to mutate the value through the reference before spewing an error.
2. Cultural convention / "the boss said so"
Some organizations enforce coding standards. These coding standards are sometimes authored by people who are possibly overly-defensive. So, you might see something like:
Unless your class is a "plain old data" type, no data members may be public. You may use getter methods for such non-public members as necessary.
and then, even if it makes sense for a specific class to just allow non-const access, it won't happen.
3. Maybe val
just isn't there?
You've given an example in which val
actually exists in an instance of the class. But actually - it doesn't have to! The get()
method could return some sort of a proxy object, which, upon assignment, mutation etc. performs some computation (e.g. storing or retrieving data in a database).
4. Allows changing class internals later without changing user code
Now, reading items 1. or 3, above, you might ask "but my struct S
does have val
!" or "by my get()
doesn't do anything interesting!" - well, true, they don't; but you might want to change this behavior in the future. Without a get()
, all of your class' users will need to change their code. With a get()
, you only need to make changes to the implementation of struct S
.
Now, I don't advocate for this kind of a design approach approach, but some programmers do.
1
"Attempt to mutate val when bad things have happened" is just incorrect. Selection ofconst
or non-const
member function depends on the qualifications of thethis
object, and not how the return value is being used. In particular, a non-const
getter cannot assume mutation is occurring.
– Ben Voigt
Dec 9 at 0:48
@BenVoigt: SupposeS
is somethingNuclearMissilePosition
, orPatientInfusionValueState
. These are mutable, and should be mutable, but under certain conditions I might not want . Now, you could say "ok, only let friend functions mutate them then", or "write a setter function then", but this is also a way to do it.
– einpoklum
Dec 9 at 10:42
But when you cannot distinguish between get and set, your error message should reflect that uncertainty, not claim mutation was attempted.
– Ben Voigt
Dec 10 at 0:30
Ok, fair enough.
– einpoklum
Dec 11 at 15:46
add a comment |
get()
is callable by non const objects which are allowed to mutate, you can do:
S r(0);
r.get() = 1;
but if you make r
const as const S r(0)
, the line r.get() = 1
no longer compile, not even to retrieve the value, that's why you need a const version const T& get() const
to at least to able to retrieve the value for const objects, doing so allows you do:
const S r(0)
int val = r.get()
The const version of member functions try to be consistent with the constness property of the object the call is made on, i.e if the object is immutable by being const and the member function returns a reference, it may reflect the constness of the caller by returning a const reference, thus preserving the immutability property of the object.
1
OP was asking, I'm sure, why have this method rather than just giving access to the member itself.
– einpoklum
Dec 8 at 14:13
1
@einpoklum He was more interested in the reason and uses cases of having two different versions ofget
to perform the same action "return val" ... check at his last 2 paragraphs.
– Jans
Dec 8 at 14:17
add a comment |
It depends on the purpose of S
. If it's some kind of a thin wrapper, it might be appropriate to allow the user to access the underlaying value directly.
One of the real-life examples is std::reference_wrapper
.
This is likely the "spot on" answer. Useful when encapsulating C types for external libraries and such.
– pipe
Dec 8 at 17:16
In my opinion,reference_wrapper
does a different thing. In OP's example, withget()
, you can modify the value of the member. Withreference_wrapper::get
, you cannot modify which object it points to, you can only modify the pointed object.
– geza
Dec 8 at 20:20
@geza reference_wrapper exposes reference semantics, so its "value" is the wrapped object. The fact it's hold by pointer is just an implementation detail.
– Igor R.
Dec 8 at 21:29
That's why it is not relevant here. In OP's example,get()
returns a reference to a member, which is contained within the object.reference_wrapper
doesn't do this., itsget()
function does something else. "Is there any case where it makes sense to have non-const get function that returns val?".reference_wrapper
returns the value pointed to, not the value itself.
– geza
Dec 8 at 21:36
add a comment |
No. If a getter simply returns a non-const reference to a member, like this:
private:
Object m_member;
public:
Object &getMember() {
return m_member;
}
Then m_member
should be public instead, and the accessor is not needed. There is absolutely no point making this member private, and then create an accessor, which gives all access to it.
If you call getMember()
, you can store the resulting reference to a pointer/reference, and afterwards, you can do whatever you want with m_member
, the enclosing class will know nothing about it. It's the same, as if m_member
had been public.
Note, that if getMember()
does some additional task (for example, it doesn't just simply return m_member
, but lazily constructs it), then getMember()
could be useful:
Object &getMember() {
if (!m_member) m_member = new Object;
return *m_member;
}
add a comment |
I would also like to point out the practical side of the question. For example, you want to aggregate your object Bar
into another object Foo
and create constant method doSomething
which uses Bar
only for reading:
class Foo
{
Bar bar_;
public:
void doSomething() const
{
//bar_.get();
}
}
You have a problem! You are absolutely sure that you do not change the Foo
object, you want to mark this by const
modificator, but you cannot do it, because get
is not const
. In other words, you create your own problems for the future and complicate the writing of clear and high-quality code. But you can implement two versions for get
if you really need it:
class Bar
{
int data = 1;
public:
int& get() { return data; };
const int& get() const { return data; };
};
such a situation can arise if you ALREADY have a code in which constant modifiers are ignored, therefore such a situation is common:
foo(int* a); // Just get a, don`t modified
foo(&bar_.get()); // need non constant get()
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53683047%2freasons-for-defining-non-const-get-member-functions%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
Non-const getters?
Getters and setters are merely convention. Instead of providing a getter and a setter, a sometimes used idiom is to provide something along the line of
struct foo {
int val() const { return val_; }
int& val() { return val_; }
private:
int val_;
};
Such that, depending on the constness of the instance you get a reference or a copy:
void bar(const foo& a, foo& b) {
auto x = a.val(); // calls the const method returning an int
b.val() = x; // calls the non-const method returning an int&
};
Whether this is good style in general is a matter of opinion. There are cases where it causes confusion and other cases where this behaviour is just what you would expect (see below).
In any case, it is more important to design the interface of a class according to what the class is supposed to do and how you want to use it rather than blindly following conventions about setters and getters (eg you should give the method a meaningful name that expresses what it does, not just in terms of "pretend to be encapsulated and now provide me access to all your internals via getters", which is what using getters everywhere actually means).
Concrete example
Consider that element access in containers is usually implemented like this. As a toy example:
struct my_array {
int operator(unsigned i) const { return data[i]; }
int& operator(unsigned i) { return data[i]; }
private:
int data[10];
};
It is not the containers job to hide the elements from the user (even data
could be public). You dont want different methods to access elements depending on whether you want to read or write the element, hence providing a const
and a non-const overload makes perfectly sense in this case.
non-const reference from get vs encapsulation
Maybe not that obvious, but it is a bit controversial whether providing getters and setters supports encapsulation or the opposite. While in general this matter is to a large extend opinion based, for getters that return non const references it is not so much about opinions. They do break encapuslation. Consider
struct broken {
void set(int x) {
counter++;
val = x;
}
int& get() { return x; }
int get() const { return x; }
private:
int counter = 0;
int value = 0;
};
This class is broken as the name suggests. Clients can simply grab a reference and the class has no chance to count the number of times the value is modified (as the set
suggests). Once you return a non-const reference then regarding encapsulation there is little difference to making the member public. Hence, this is used only for cases where such behaviour is natural (eg container).
PS
Note that your example returns a const T&
rather than a value. This is reasonable for template code, where you dont know how expensive a copy is, while for an int
you wont gain much by returning a const int&
instead of an int
. For the sake of clarity I used non-template examples, though for templated code you would probably rather return a const T&
.
add a comment |
Non-const getters?
Getters and setters are merely convention. Instead of providing a getter and a setter, a sometimes used idiom is to provide something along the line of
struct foo {
int val() const { return val_; }
int& val() { return val_; }
private:
int val_;
};
Such that, depending on the constness of the instance you get a reference or a copy:
void bar(const foo& a, foo& b) {
auto x = a.val(); // calls the const method returning an int
b.val() = x; // calls the non-const method returning an int&
};
Whether this is good style in general is a matter of opinion. There are cases where it causes confusion and other cases where this behaviour is just what you would expect (see below).
In any case, it is more important to design the interface of a class according to what the class is supposed to do and how you want to use it rather than blindly following conventions about setters and getters (eg you should give the method a meaningful name that expresses what it does, not just in terms of "pretend to be encapsulated and now provide me access to all your internals via getters", which is what using getters everywhere actually means).
Concrete example
Consider that element access in containers is usually implemented like this. As a toy example:
struct my_array {
int operator(unsigned i) const { return data[i]; }
int& operator(unsigned i) { return data[i]; }
private:
int data[10];
};
It is not the containers job to hide the elements from the user (even data
could be public). You dont want different methods to access elements depending on whether you want to read or write the element, hence providing a const
and a non-const overload makes perfectly sense in this case.
non-const reference from get vs encapsulation
Maybe not that obvious, but it is a bit controversial whether providing getters and setters supports encapsulation or the opposite. While in general this matter is to a large extend opinion based, for getters that return non const references it is not so much about opinions. They do break encapuslation. Consider
struct broken {
void set(int x) {
counter++;
val = x;
}
int& get() { return x; }
int get() const { return x; }
private:
int counter = 0;
int value = 0;
};
This class is broken as the name suggests. Clients can simply grab a reference and the class has no chance to count the number of times the value is modified (as the set
suggests). Once you return a non-const reference then regarding encapsulation there is little difference to making the member public. Hence, this is used only for cases where such behaviour is natural (eg container).
PS
Note that your example returns a const T&
rather than a value. This is reasonable for template code, where you dont know how expensive a copy is, while for an int
you wont gain much by returning a const int&
instead of an int
. For the sake of clarity I used non-template examples, though for templated code you would probably rather return a const T&
.
add a comment |
Non-const getters?
Getters and setters are merely convention. Instead of providing a getter and a setter, a sometimes used idiom is to provide something along the line of
struct foo {
int val() const { return val_; }
int& val() { return val_; }
private:
int val_;
};
Such that, depending on the constness of the instance you get a reference or a copy:
void bar(const foo& a, foo& b) {
auto x = a.val(); // calls the const method returning an int
b.val() = x; // calls the non-const method returning an int&
};
Whether this is good style in general is a matter of opinion. There are cases where it causes confusion and other cases where this behaviour is just what you would expect (see below).
In any case, it is more important to design the interface of a class according to what the class is supposed to do and how you want to use it rather than blindly following conventions about setters and getters (eg you should give the method a meaningful name that expresses what it does, not just in terms of "pretend to be encapsulated and now provide me access to all your internals via getters", which is what using getters everywhere actually means).
Concrete example
Consider that element access in containers is usually implemented like this. As a toy example:
struct my_array {
int operator(unsigned i) const { return data[i]; }
int& operator(unsigned i) { return data[i]; }
private:
int data[10];
};
It is not the containers job to hide the elements from the user (even data
could be public). You dont want different methods to access elements depending on whether you want to read or write the element, hence providing a const
and a non-const overload makes perfectly sense in this case.
non-const reference from get vs encapsulation
Maybe not that obvious, but it is a bit controversial whether providing getters and setters supports encapsulation or the opposite. While in general this matter is to a large extend opinion based, for getters that return non const references it is not so much about opinions. They do break encapuslation. Consider
struct broken {
void set(int x) {
counter++;
val = x;
}
int& get() { return x; }
int get() const { return x; }
private:
int counter = 0;
int value = 0;
};
This class is broken as the name suggests. Clients can simply grab a reference and the class has no chance to count the number of times the value is modified (as the set
suggests). Once you return a non-const reference then regarding encapsulation there is little difference to making the member public. Hence, this is used only for cases where such behaviour is natural (eg container).
PS
Note that your example returns a const T&
rather than a value. This is reasonable for template code, where you dont know how expensive a copy is, while for an int
you wont gain much by returning a const int&
instead of an int
. For the sake of clarity I used non-template examples, though for templated code you would probably rather return a const T&
.
Non-const getters?
Getters and setters are merely convention. Instead of providing a getter and a setter, a sometimes used idiom is to provide something along the line of
struct foo {
int val() const { return val_; }
int& val() { return val_; }
private:
int val_;
};
Such that, depending on the constness of the instance you get a reference or a copy:
void bar(const foo& a, foo& b) {
auto x = a.val(); // calls the const method returning an int
b.val() = x; // calls the non-const method returning an int&
};
Whether this is good style in general is a matter of opinion. There are cases where it causes confusion and other cases where this behaviour is just what you would expect (see below).
In any case, it is more important to design the interface of a class according to what the class is supposed to do and how you want to use it rather than blindly following conventions about setters and getters (eg you should give the method a meaningful name that expresses what it does, not just in terms of "pretend to be encapsulated and now provide me access to all your internals via getters", which is what using getters everywhere actually means).
Concrete example
Consider that element access in containers is usually implemented like this. As a toy example:
struct my_array {
int operator(unsigned i) const { return data[i]; }
int& operator(unsigned i) { return data[i]; }
private:
int data[10];
};
It is not the containers job to hide the elements from the user (even data
could be public). You dont want different methods to access elements depending on whether you want to read or write the element, hence providing a const
and a non-const overload makes perfectly sense in this case.
non-const reference from get vs encapsulation
Maybe not that obvious, but it is a bit controversial whether providing getters and setters supports encapsulation or the opposite. While in general this matter is to a large extend opinion based, for getters that return non const references it is not so much about opinions. They do break encapuslation. Consider
struct broken {
void set(int x) {
counter++;
val = x;
}
int& get() { return x; }
int get() const { return x; }
private:
int counter = 0;
int value = 0;
};
This class is broken as the name suggests. Clients can simply grab a reference and the class has no chance to count the number of times the value is modified (as the set
suggests). Once you return a non-const reference then regarding encapsulation there is little difference to making the member public. Hence, this is used only for cases where such behaviour is natural (eg container).
PS
Note that your example returns a const T&
rather than a value. This is reasonable for template code, where you dont know how expensive a copy is, while for an int
you wont gain much by returning a const int&
instead of an int
. For the sake of clarity I used non-template examples, though for templated code you would probably rather return a const T&
.
edited Dec 13 at 19:55
answered Dec 8 at 13:43
user463035818
16.5k42663
16.5k42663
add a comment |
add a comment |
First let me rephrase your question:
Why have a non-const getter for a member, rather than just making the member public?
Several possible reasons reasons:
1. Easy to instrument
Who said the non-const getter needs to be just:
T& get() { return val; }
? it could well be something like:
T& get() {
if (check_for_something_bad()) {
throw std::runtime_error{"Attempt to mutate val when bad things have happened");
}
return val;
}
However, as @BenVoigt suggests, it is more appropriate to wait until the caller actually tries to mutate the value through the reference before spewing an error.
2. Cultural convention / "the boss said so"
Some organizations enforce coding standards. These coding standards are sometimes authored by people who are possibly overly-defensive. So, you might see something like:
Unless your class is a "plain old data" type, no data members may be public. You may use getter methods for such non-public members as necessary.
and then, even if it makes sense for a specific class to just allow non-const access, it won't happen.
3. Maybe val
just isn't there?
You've given an example in which val
actually exists in an instance of the class. But actually - it doesn't have to! The get()
method could return some sort of a proxy object, which, upon assignment, mutation etc. performs some computation (e.g. storing or retrieving data in a database).
4. Allows changing class internals later without changing user code
Now, reading items 1. or 3, above, you might ask "but my struct S
does have val
!" or "by my get()
doesn't do anything interesting!" - well, true, they don't; but you might want to change this behavior in the future. Without a get()
, all of your class' users will need to change their code. With a get()
, you only need to make changes to the implementation of struct S
.
Now, I don't advocate for this kind of a design approach approach, but some programmers do.
1
"Attempt to mutate val when bad things have happened" is just incorrect. Selection ofconst
or non-const
member function depends on the qualifications of thethis
object, and not how the return value is being used. In particular, a non-const
getter cannot assume mutation is occurring.
– Ben Voigt
Dec 9 at 0:48
@BenVoigt: SupposeS
is somethingNuclearMissilePosition
, orPatientInfusionValueState
. These are mutable, and should be mutable, but under certain conditions I might not want . Now, you could say "ok, only let friend functions mutate them then", or "write a setter function then", but this is also a way to do it.
– einpoklum
Dec 9 at 10:42
But when you cannot distinguish between get and set, your error message should reflect that uncertainty, not claim mutation was attempted.
– Ben Voigt
Dec 10 at 0:30
Ok, fair enough.
– einpoklum
Dec 11 at 15:46
add a comment |
First let me rephrase your question:
Why have a non-const getter for a member, rather than just making the member public?
Several possible reasons reasons:
1. Easy to instrument
Who said the non-const getter needs to be just:
T& get() { return val; }
? it could well be something like:
T& get() {
if (check_for_something_bad()) {
throw std::runtime_error{"Attempt to mutate val when bad things have happened");
}
return val;
}
However, as @BenVoigt suggests, it is more appropriate to wait until the caller actually tries to mutate the value through the reference before spewing an error.
2. Cultural convention / "the boss said so"
Some organizations enforce coding standards. These coding standards are sometimes authored by people who are possibly overly-defensive. So, you might see something like:
Unless your class is a "plain old data" type, no data members may be public. You may use getter methods for such non-public members as necessary.
and then, even if it makes sense for a specific class to just allow non-const access, it won't happen.
3. Maybe val
just isn't there?
You've given an example in which val
actually exists in an instance of the class. But actually - it doesn't have to! The get()
method could return some sort of a proxy object, which, upon assignment, mutation etc. performs some computation (e.g. storing or retrieving data in a database).
4. Allows changing class internals later without changing user code
Now, reading items 1. or 3, above, you might ask "but my struct S
does have val
!" or "by my get()
doesn't do anything interesting!" - well, true, they don't; but you might want to change this behavior in the future. Without a get()
, all of your class' users will need to change their code. With a get()
, you only need to make changes to the implementation of struct S
.
Now, I don't advocate for this kind of a design approach approach, but some programmers do.
1
"Attempt to mutate val when bad things have happened" is just incorrect. Selection ofconst
or non-const
member function depends on the qualifications of thethis
object, and not how the return value is being used. In particular, a non-const
getter cannot assume mutation is occurring.
– Ben Voigt
Dec 9 at 0:48
@BenVoigt: SupposeS
is somethingNuclearMissilePosition
, orPatientInfusionValueState
. These are mutable, and should be mutable, but under certain conditions I might not want . Now, you could say "ok, only let friend functions mutate them then", or "write a setter function then", but this is also a way to do it.
– einpoklum
Dec 9 at 10:42
But when you cannot distinguish between get and set, your error message should reflect that uncertainty, not claim mutation was attempted.
– Ben Voigt
Dec 10 at 0:30
Ok, fair enough.
– einpoklum
Dec 11 at 15:46
add a comment |
First let me rephrase your question:
Why have a non-const getter for a member, rather than just making the member public?
Several possible reasons reasons:
1. Easy to instrument
Who said the non-const getter needs to be just:
T& get() { return val; }
? it could well be something like:
T& get() {
if (check_for_something_bad()) {
throw std::runtime_error{"Attempt to mutate val when bad things have happened");
}
return val;
}
However, as @BenVoigt suggests, it is more appropriate to wait until the caller actually tries to mutate the value through the reference before spewing an error.
2. Cultural convention / "the boss said so"
Some organizations enforce coding standards. These coding standards are sometimes authored by people who are possibly overly-defensive. So, you might see something like:
Unless your class is a "plain old data" type, no data members may be public. You may use getter methods for such non-public members as necessary.
and then, even if it makes sense for a specific class to just allow non-const access, it won't happen.
3. Maybe val
just isn't there?
You've given an example in which val
actually exists in an instance of the class. But actually - it doesn't have to! The get()
method could return some sort of a proxy object, which, upon assignment, mutation etc. performs some computation (e.g. storing or retrieving data in a database).
4. Allows changing class internals later without changing user code
Now, reading items 1. or 3, above, you might ask "but my struct S
does have val
!" or "by my get()
doesn't do anything interesting!" - well, true, they don't; but you might want to change this behavior in the future. Without a get()
, all of your class' users will need to change their code. With a get()
, you only need to make changes to the implementation of struct S
.
Now, I don't advocate for this kind of a design approach approach, but some programmers do.
First let me rephrase your question:
Why have a non-const getter for a member, rather than just making the member public?
Several possible reasons reasons:
1. Easy to instrument
Who said the non-const getter needs to be just:
T& get() { return val; }
? it could well be something like:
T& get() {
if (check_for_something_bad()) {
throw std::runtime_error{"Attempt to mutate val when bad things have happened");
}
return val;
}
However, as @BenVoigt suggests, it is more appropriate to wait until the caller actually tries to mutate the value through the reference before spewing an error.
2. Cultural convention / "the boss said so"
Some organizations enforce coding standards. These coding standards are sometimes authored by people who are possibly overly-defensive. So, you might see something like:
Unless your class is a "plain old data" type, no data members may be public. You may use getter methods for such non-public members as necessary.
and then, even if it makes sense for a specific class to just allow non-const access, it won't happen.
3. Maybe val
just isn't there?
You've given an example in which val
actually exists in an instance of the class. But actually - it doesn't have to! The get()
method could return some sort of a proxy object, which, upon assignment, mutation etc. performs some computation (e.g. storing or retrieving data in a database).
4. Allows changing class internals later without changing user code
Now, reading items 1. or 3, above, you might ask "but my struct S
does have val
!" or "by my get()
doesn't do anything interesting!" - well, true, they don't; but you might want to change this behavior in the future. Without a get()
, all of your class' users will need to change their code. With a get()
, you only need to make changes to the implementation of struct S
.
Now, I don't advocate for this kind of a design approach approach, but some programmers do.
edited Dec 11 at 15:45
answered Dec 8 at 14:33
einpoklum
33.3k26118234
33.3k26118234
1
"Attempt to mutate val when bad things have happened" is just incorrect. Selection ofconst
or non-const
member function depends on the qualifications of thethis
object, and not how the return value is being used. In particular, a non-const
getter cannot assume mutation is occurring.
– Ben Voigt
Dec 9 at 0:48
@BenVoigt: SupposeS
is somethingNuclearMissilePosition
, orPatientInfusionValueState
. These are mutable, and should be mutable, but under certain conditions I might not want . Now, you could say "ok, only let friend functions mutate them then", or "write a setter function then", but this is also a way to do it.
– einpoklum
Dec 9 at 10:42
But when you cannot distinguish between get and set, your error message should reflect that uncertainty, not claim mutation was attempted.
– Ben Voigt
Dec 10 at 0:30
Ok, fair enough.
– einpoklum
Dec 11 at 15:46
add a comment |
1
"Attempt to mutate val when bad things have happened" is just incorrect. Selection ofconst
or non-const
member function depends on the qualifications of thethis
object, and not how the return value is being used. In particular, a non-const
getter cannot assume mutation is occurring.
– Ben Voigt
Dec 9 at 0:48
@BenVoigt: SupposeS
is somethingNuclearMissilePosition
, orPatientInfusionValueState
. These are mutable, and should be mutable, but under certain conditions I might not want . Now, you could say "ok, only let friend functions mutate them then", or "write a setter function then", but this is also a way to do it.
– einpoklum
Dec 9 at 10:42
But when you cannot distinguish between get and set, your error message should reflect that uncertainty, not claim mutation was attempted.
– Ben Voigt
Dec 10 at 0:30
Ok, fair enough.
– einpoklum
Dec 11 at 15:46
1
1
"Attempt to mutate val when bad things have happened" is just incorrect. Selection of
const
or non-const
member function depends on the qualifications of the this
object, and not how the return value is being used. In particular, a non-const
getter cannot assume mutation is occurring.– Ben Voigt
Dec 9 at 0:48
"Attempt to mutate val when bad things have happened" is just incorrect. Selection of
const
or non-const
member function depends on the qualifications of the this
object, and not how the return value is being used. In particular, a non-const
getter cannot assume mutation is occurring.– Ben Voigt
Dec 9 at 0:48
@BenVoigt: Suppose
S
is something NuclearMissilePosition
, or PatientInfusionValueState
. These are mutable, and should be mutable, but under certain conditions I might not want . Now, you could say "ok, only let friend functions mutate them then", or "write a setter function then", but this is also a way to do it.– einpoklum
Dec 9 at 10:42
@BenVoigt: Suppose
S
is something NuclearMissilePosition
, or PatientInfusionValueState
. These are mutable, and should be mutable, but under certain conditions I might not want . Now, you could say "ok, only let friend functions mutate them then", or "write a setter function then", but this is also a way to do it.– einpoklum
Dec 9 at 10:42
But when you cannot distinguish between get and set, your error message should reflect that uncertainty, not claim mutation was attempted.
– Ben Voigt
Dec 10 at 0:30
But when you cannot distinguish between get and set, your error message should reflect that uncertainty, not claim mutation was attempted.
– Ben Voigt
Dec 10 at 0:30
Ok, fair enough.
– einpoklum
Dec 11 at 15:46
Ok, fair enough.
– einpoklum
Dec 11 at 15:46
add a comment |
get()
is callable by non const objects which are allowed to mutate, you can do:
S r(0);
r.get() = 1;
but if you make r
const as const S r(0)
, the line r.get() = 1
no longer compile, not even to retrieve the value, that's why you need a const version const T& get() const
to at least to able to retrieve the value for const objects, doing so allows you do:
const S r(0)
int val = r.get()
The const version of member functions try to be consistent with the constness property of the object the call is made on, i.e if the object is immutable by being const and the member function returns a reference, it may reflect the constness of the caller by returning a const reference, thus preserving the immutability property of the object.
1
OP was asking, I'm sure, why have this method rather than just giving access to the member itself.
– einpoklum
Dec 8 at 14:13
1
@einpoklum He was more interested in the reason and uses cases of having two different versions ofget
to perform the same action "return val" ... check at his last 2 paragraphs.
– Jans
Dec 8 at 14:17
add a comment |
get()
is callable by non const objects which are allowed to mutate, you can do:
S r(0);
r.get() = 1;
but if you make r
const as const S r(0)
, the line r.get() = 1
no longer compile, not even to retrieve the value, that's why you need a const version const T& get() const
to at least to able to retrieve the value for const objects, doing so allows you do:
const S r(0)
int val = r.get()
The const version of member functions try to be consistent with the constness property of the object the call is made on, i.e if the object is immutable by being const and the member function returns a reference, it may reflect the constness of the caller by returning a const reference, thus preserving the immutability property of the object.
1
OP was asking, I'm sure, why have this method rather than just giving access to the member itself.
– einpoklum
Dec 8 at 14:13
1
@einpoklum He was more interested in the reason and uses cases of having two different versions ofget
to perform the same action "return val" ... check at his last 2 paragraphs.
– Jans
Dec 8 at 14:17
add a comment |
get()
is callable by non const objects which are allowed to mutate, you can do:
S r(0);
r.get() = 1;
but if you make r
const as const S r(0)
, the line r.get() = 1
no longer compile, not even to retrieve the value, that's why you need a const version const T& get() const
to at least to able to retrieve the value for const objects, doing so allows you do:
const S r(0)
int val = r.get()
The const version of member functions try to be consistent with the constness property of the object the call is made on, i.e if the object is immutable by being const and the member function returns a reference, it may reflect the constness of the caller by returning a const reference, thus preserving the immutability property of the object.
get()
is callable by non const objects which are allowed to mutate, you can do:
S r(0);
r.get() = 1;
but if you make r
const as const S r(0)
, the line r.get() = 1
no longer compile, not even to retrieve the value, that's why you need a const version const T& get() const
to at least to able to retrieve the value for const objects, doing so allows you do:
const S r(0)
int val = r.get()
The const version of member functions try to be consistent with the constness property of the object the call is made on, i.e if the object is immutable by being const and the member function returns a reference, it may reflect the constness of the caller by returning a const reference, thus preserving the immutability property of the object.
edited Dec 8 at 17:51
answered Dec 8 at 13:41
Jans
8,07522535
8,07522535
1
OP was asking, I'm sure, why have this method rather than just giving access to the member itself.
– einpoklum
Dec 8 at 14:13
1
@einpoklum He was more interested in the reason and uses cases of having two different versions ofget
to perform the same action "return val" ... check at his last 2 paragraphs.
– Jans
Dec 8 at 14:17
add a comment |
1
OP was asking, I'm sure, why have this method rather than just giving access to the member itself.
– einpoklum
Dec 8 at 14:13
1
@einpoklum He was more interested in the reason and uses cases of having two different versions ofget
to perform the same action "return val" ... check at his last 2 paragraphs.
– Jans
Dec 8 at 14:17
1
1
OP was asking, I'm sure, why have this method rather than just giving access to the member itself.
– einpoklum
Dec 8 at 14:13
OP was asking, I'm sure, why have this method rather than just giving access to the member itself.
– einpoklum
Dec 8 at 14:13
1
1
@einpoklum He was more interested in the reason and uses cases of having two different versions of
get
to perform the same action "return val" ... check at his last 2 paragraphs.– Jans
Dec 8 at 14:17
@einpoklum He was more interested in the reason and uses cases of having two different versions of
get
to perform the same action "return val" ... check at his last 2 paragraphs.– Jans
Dec 8 at 14:17
add a comment |
It depends on the purpose of S
. If it's some kind of a thin wrapper, it might be appropriate to allow the user to access the underlaying value directly.
One of the real-life examples is std::reference_wrapper
.
This is likely the "spot on" answer. Useful when encapsulating C types for external libraries and such.
– pipe
Dec 8 at 17:16
In my opinion,reference_wrapper
does a different thing. In OP's example, withget()
, you can modify the value of the member. Withreference_wrapper::get
, you cannot modify which object it points to, you can only modify the pointed object.
– geza
Dec 8 at 20:20
@geza reference_wrapper exposes reference semantics, so its "value" is the wrapped object. The fact it's hold by pointer is just an implementation detail.
– Igor R.
Dec 8 at 21:29
That's why it is not relevant here. In OP's example,get()
returns a reference to a member, which is contained within the object.reference_wrapper
doesn't do this., itsget()
function does something else. "Is there any case where it makes sense to have non-const get function that returns val?".reference_wrapper
returns the value pointed to, not the value itself.
– geza
Dec 8 at 21:36
add a comment |
It depends on the purpose of S
. If it's some kind of a thin wrapper, it might be appropriate to allow the user to access the underlaying value directly.
One of the real-life examples is std::reference_wrapper
.
This is likely the "spot on" answer. Useful when encapsulating C types for external libraries and such.
– pipe
Dec 8 at 17:16
In my opinion,reference_wrapper
does a different thing. In OP's example, withget()
, you can modify the value of the member. Withreference_wrapper::get
, you cannot modify which object it points to, you can only modify the pointed object.
– geza
Dec 8 at 20:20
@geza reference_wrapper exposes reference semantics, so its "value" is the wrapped object. The fact it's hold by pointer is just an implementation detail.
– Igor R.
Dec 8 at 21:29
That's why it is not relevant here. In OP's example,get()
returns a reference to a member, which is contained within the object.reference_wrapper
doesn't do this., itsget()
function does something else. "Is there any case where it makes sense to have non-const get function that returns val?".reference_wrapper
returns the value pointed to, not the value itself.
– geza
Dec 8 at 21:36
add a comment |
It depends on the purpose of S
. If it's some kind of a thin wrapper, it might be appropriate to allow the user to access the underlaying value directly.
One of the real-life examples is std::reference_wrapper
.
It depends on the purpose of S
. If it's some kind of a thin wrapper, it might be appropriate to allow the user to access the underlaying value directly.
One of the real-life examples is std::reference_wrapper
.
answered Dec 8 at 13:48
Igor R.
10.6k12959
10.6k12959
This is likely the "spot on" answer. Useful when encapsulating C types for external libraries and such.
– pipe
Dec 8 at 17:16
In my opinion,reference_wrapper
does a different thing. In OP's example, withget()
, you can modify the value of the member. Withreference_wrapper::get
, you cannot modify which object it points to, you can only modify the pointed object.
– geza
Dec 8 at 20:20
@geza reference_wrapper exposes reference semantics, so its "value" is the wrapped object. The fact it's hold by pointer is just an implementation detail.
– Igor R.
Dec 8 at 21:29
That's why it is not relevant here. In OP's example,get()
returns a reference to a member, which is contained within the object.reference_wrapper
doesn't do this., itsget()
function does something else. "Is there any case where it makes sense to have non-const get function that returns val?".reference_wrapper
returns the value pointed to, not the value itself.
– geza
Dec 8 at 21:36
add a comment |
This is likely the "spot on" answer. Useful when encapsulating C types for external libraries and such.
– pipe
Dec 8 at 17:16
In my opinion,reference_wrapper
does a different thing. In OP's example, withget()
, you can modify the value of the member. Withreference_wrapper::get
, you cannot modify which object it points to, you can only modify the pointed object.
– geza
Dec 8 at 20:20
@geza reference_wrapper exposes reference semantics, so its "value" is the wrapped object. The fact it's hold by pointer is just an implementation detail.
– Igor R.
Dec 8 at 21:29
That's why it is not relevant here. In OP's example,get()
returns a reference to a member, which is contained within the object.reference_wrapper
doesn't do this., itsget()
function does something else. "Is there any case where it makes sense to have non-const get function that returns val?".reference_wrapper
returns the value pointed to, not the value itself.
– geza
Dec 8 at 21:36
This is likely the "spot on" answer. Useful when encapsulating C types for external libraries and such.
– pipe
Dec 8 at 17:16
This is likely the "spot on" answer. Useful when encapsulating C types for external libraries and such.
– pipe
Dec 8 at 17:16
In my opinion,
reference_wrapper
does a different thing. In OP's example, with get()
, you can modify the value of the member. With reference_wrapper::get
, you cannot modify which object it points to, you can only modify the pointed object.– geza
Dec 8 at 20:20
In my opinion,
reference_wrapper
does a different thing. In OP's example, with get()
, you can modify the value of the member. With reference_wrapper::get
, you cannot modify which object it points to, you can only modify the pointed object.– geza
Dec 8 at 20:20
@geza reference_wrapper exposes reference semantics, so its "value" is the wrapped object. The fact it's hold by pointer is just an implementation detail.
– Igor R.
Dec 8 at 21:29
@geza reference_wrapper exposes reference semantics, so its "value" is the wrapped object. The fact it's hold by pointer is just an implementation detail.
– Igor R.
Dec 8 at 21:29
That's why it is not relevant here. In OP's example,
get()
returns a reference to a member, which is contained within the object. reference_wrapper
doesn't do this., its get()
function does something else. "Is there any case where it makes sense to have non-const get function that returns val?". reference_wrapper
returns the value pointed to, not the value itself.– geza
Dec 8 at 21:36
That's why it is not relevant here. In OP's example,
get()
returns a reference to a member, which is contained within the object. reference_wrapper
doesn't do this., its get()
function does something else. "Is there any case where it makes sense to have non-const get function that returns val?". reference_wrapper
returns the value pointed to, not the value itself.– geza
Dec 8 at 21:36
add a comment |
No. If a getter simply returns a non-const reference to a member, like this:
private:
Object m_member;
public:
Object &getMember() {
return m_member;
}
Then m_member
should be public instead, and the accessor is not needed. There is absolutely no point making this member private, and then create an accessor, which gives all access to it.
If you call getMember()
, you can store the resulting reference to a pointer/reference, and afterwards, you can do whatever you want with m_member
, the enclosing class will know nothing about it. It's the same, as if m_member
had been public.
Note, that if getMember()
does some additional task (for example, it doesn't just simply return m_member
, but lazily constructs it), then getMember()
could be useful:
Object &getMember() {
if (!m_member) m_member = new Object;
return *m_member;
}
add a comment |
No. If a getter simply returns a non-const reference to a member, like this:
private:
Object m_member;
public:
Object &getMember() {
return m_member;
}
Then m_member
should be public instead, and the accessor is not needed. There is absolutely no point making this member private, and then create an accessor, which gives all access to it.
If you call getMember()
, you can store the resulting reference to a pointer/reference, and afterwards, you can do whatever you want with m_member
, the enclosing class will know nothing about it. It's the same, as if m_member
had been public.
Note, that if getMember()
does some additional task (for example, it doesn't just simply return m_member
, but lazily constructs it), then getMember()
could be useful:
Object &getMember() {
if (!m_member) m_member = new Object;
return *m_member;
}
add a comment |
No. If a getter simply returns a non-const reference to a member, like this:
private:
Object m_member;
public:
Object &getMember() {
return m_member;
}
Then m_member
should be public instead, and the accessor is not needed. There is absolutely no point making this member private, and then create an accessor, which gives all access to it.
If you call getMember()
, you can store the resulting reference to a pointer/reference, and afterwards, you can do whatever you want with m_member
, the enclosing class will know nothing about it. It's the same, as if m_member
had been public.
Note, that if getMember()
does some additional task (for example, it doesn't just simply return m_member
, but lazily constructs it), then getMember()
could be useful:
Object &getMember() {
if (!m_member) m_member = new Object;
return *m_member;
}
No. If a getter simply returns a non-const reference to a member, like this:
private:
Object m_member;
public:
Object &getMember() {
return m_member;
}
Then m_member
should be public instead, and the accessor is not needed. There is absolutely no point making this member private, and then create an accessor, which gives all access to it.
If you call getMember()
, you can store the resulting reference to a pointer/reference, and afterwards, you can do whatever you want with m_member
, the enclosing class will know nothing about it. It's the same, as if m_member
had been public.
Note, that if getMember()
does some additional task (for example, it doesn't just simply return m_member
, but lazily constructs it), then getMember()
could be useful:
Object &getMember() {
if (!m_member) m_member = new Object;
return *m_member;
}
answered Dec 8 at 14:17
geza
12.6k32775
12.6k32775
add a comment |
add a comment |
I would also like to point out the practical side of the question. For example, you want to aggregate your object Bar
into another object Foo
and create constant method doSomething
which uses Bar
only for reading:
class Foo
{
Bar bar_;
public:
void doSomething() const
{
//bar_.get();
}
}
You have a problem! You are absolutely sure that you do not change the Foo
object, you want to mark this by const
modificator, but you cannot do it, because get
is not const
. In other words, you create your own problems for the future and complicate the writing of clear and high-quality code. But you can implement two versions for get
if you really need it:
class Bar
{
int data = 1;
public:
int& get() { return data; };
const int& get() const { return data; };
};
such a situation can arise if you ALREADY have a code in which constant modifiers are ignored, therefore such a situation is common:
foo(int* a); // Just get a, don`t modified
foo(&bar_.get()); // need non constant get()
add a comment |
I would also like to point out the practical side of the question. For example, you want to aggregate your object Bar
into another object Foo
and create constant method doSomething
which uses Bar
only for reading:
class Foo
{
Bar bar_;
public:
void doSomething() const
{
//bar_.get();
}
}
You have a problem! You are absolutely sure that you do not change the Foo
object, you want to mark this by const
modificator, but you cannot do it, because get
is not const
. In other words, you create your own problems for the future and complicate the writing of clear and high-quality code. But you can implement two versions for get
if you really need it:
class Bar
{
int data = 1;
public:
int& get() { return data; };
const int& get() const { return data; };
};
such a situation can arise if you ALREADY have a code in which constant modifiers are ignored, therefore such a situation is common:
foo(int* a); // Just get a, don`t modified
foo(&bar_.get()); // need non constant get()
add a comment |
I would also like to point out the practical side of the question. For example, you want to aggregate your object Bar
into another object Foo
and create constant method doSomething
which uses Bar
only for reading:
class Foo
{
Bar bar_;
public:
void doSomething() const
{
//bar_.get();
}
}
You have a problem! You are absolutely sure that you do not change the Foo
object, you want to mark this by const
modificator, but you cannot do it, because get
is not const
. In other words, you create your own problems for the future and complicate the writing of clear and high-quality code. But you can implement two versions for get
if you really need it:
class Bar
{
int data = 1;
public:
int& get() { return data; };
const int& get() const { return data; };
};
such a situation can arise if you ALREADY have a code in which constant modifiers are ignored, therefore such a situation is common:
foo(int* a); // Just get a, don`t modified
foo(&bar_.get()); // need non constant get()
I would also like to point out the practical side of the question. For example, you want to aggregate your object Bar
into another object Foo
and create constant method doSomething
which uses Bar
only for reading:
class Foo
{
Bar bar_;
public:
void doSomething() const
{
//bar_.get();
}
}
You have a problem! You are absolutely sure that you do not change the Foo
object, you want to mark this by const
modificator, but you cannot do it, because get
is not const
. In other words, you create your own problems for the future and complicate the writing of clear and high-quality code. But you can implement two versions for get
if you really need it:
class Bar
{
int data = 1;
public:
int& get() { return data; };
const int& get() const { return data; };
};
such a situation can arise if you ALREADY have a code in which constant modifiers are ignored, therefore such a situation is common:
foo(int* a); // Just get a, don`t modified
foo(&bar_.get()); // need non constant get()
edited Dec 8 at 14:59
answered Dec 8 at 14:46
Dmytro Dadyka
6931318
6931318
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53683047%2freasons-for-defining-non-const-get-member-functions%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
9
IMO even cleaner would be no getters or setters at all, and instead have the class be about behavior instead. If you have getters and setters for a member variable, you might as well make it
public
.– Some programmer dude
Dec 8 at 13:40
1
The primary reason for using a non-const get member function (or any getter and setter anti-pattern) is to avoid doing proper object-oriented programming.
– Eljay
Dec 8 at 14:16
2
@Someprogrammerdude Not really. Maybe right now that field is just a plain value but you know that when you are going to iterate to develop new requirements it will have to be a computed value, and thus having a getter avoids having to change all the code that accesses the field. Also: having a getter you can easily add a breakpoint on it and check in a debugger where is the code that accesses it. If you have just a field you have to put breakpoints in every place that accesses the field which is much more cumbersome to do.
– Bakuriu
Dec 8 at 14:49
A const getter makes sense if the setter had to make some checks (i.e. establish invariants) before setting the value which, in cases when it needs an invariant, is usually made private to allow modification through a constructor and a setter only (which would guarantee the invariants). In general, in my experience setters and getters, and especially non-const getters, are more often a result of oral and written tradition rather than a conclusion of thoughts specific to a particular case. However, notice that Stroustrup's example is a generic case meant to simply introduce the idea.
– Leo Heinsaar
Dec 9 at 11:22
@Bakuriu I guess what Some was refering to is that other classes accessing private fields is bad design in the first place. Just a stupid example: bad design is to have a
customer::getMoney()
andcustomer::setMoney(int)
when it actually should be acustomer::pay(int)
. All your concerns are covered bypay
plus you dont break encapsulation– user463035818
Dec 13 at 12:21