Return pointer-to-member in const method
-
class Foo { int member; int *pointerToMember() const { return &member; } }
gcc 9.3.0:
error: cannot initialize return object of type 'int *' with an rvalue of type 'const int *'
The declaration of the method as
const
prevents me returning&member
. I might use that pointer later (code here is simplest, not real example) for write access tomember
, so I don't want to returnconst int *
, but this method itself does not altermember
.Could someone explain what this rule is on a
const
class method? If possible, can you supply a C++ reference page on this aspect ofconst
so I can read up? Thanks. -
@Christian-Ehrlicher said in Return pointer-to-member in const method:
Implement the non-const version and call it in the const version (but may lead to an unneeded detach)
Yes, I will think about that. I never cared about copy-on-write in this function's case.
implement it twice
LOL. That's not a solution, it's a workaround! You saw my lookup code, I'm not duplicating that!
don't use a cow container
No cows anywhere in my code....? Oohhhh, sorry, got it...
use a template:
I will indeed look at your code tomorrow, I had a feeling templates might come into it.
TBH, all I really want here, when I think about, is to be allowed to call a non-
const
member method from aconst
member function, in this case. My non-const
function doesn't alter anything --- only returns a pointer to internal which might be used to write into by caller. But I won't be doing any such thing when calling from aconst
member. I (think I) want a newsemi_const
keyword, at least for a method, which does just promise not to alter the state of*this
. That's all I thoughtconst method()
did when I started this thread. Is that so much to ask for? :)Simplified it the template little bit more:
class foo { template <typename F> static auto getFooInternal(F *f, int idx) { return &f->m_foo[idx]; } public: s* getFoo(int idx) { return getFooInternal(this, idx); } const s* getFoo(int idx) const { return getFooInternal(this, idx); } private: QVector<s> m_foo; };
-
class Foo { int member; int *pointerToMember() const { return &member; } }
gcc 9.3.0:
error: cannot initialize return object of type 'int *' with an rvalue of type 'const int *'
The declaration of the method as
const
prevents me returning&member
. I might use that pointer later (code here is simplest, not real example) for write access tomember
, so I don't want to returnconst int *
, but this method itself does not altermember
.Could someone explain what this rule is on a
const
class method? If possible, can you supply a C++ reference page on this aspect ofconst
so I can read up? Thanks.@JonB said in Return pointer-to-member in const method:
int *pointerToMember() const { return &member; }
const int *pointerToMember() const { return &member; }
Else you would be able to change the state of the object using the pointer even though the method is marked as const.
-
@JonB said in Return pointer-to-member in const method:
int *pointerToMember() const { return &member; }
const int *pointerToMember() const { return &member; }
Else you would be able to change the state of the object using the pointer even though the method is marked as const.
@jsulm
But I know this, which is why I wroteso I don't want to return
const int *
, but this method itself does not alter memberI was asking (nicely) if you have a reference to this aspect of
const
method in C++ I could read up on? I do understand aconst
method cannot alter a member, but in itself this method does not do so.Let me try this: sometimes I use this return result just for read, sometimes I want to write back into the pointed-to. Obviously if I just remove the
const
off the method, I cannot call it in the read-only case from some other classconst
method.How to achieve? Do I have to provide two methods:
int *pointerToMember() { return &member; } const int *pointerToMember() const { return &member; }
I have a feeling you may answer "yes" to that?
Supposing you do: assume the body of the method is several lines long. [For the record, it's actually a look-up in a member array of
struct
s, and I want to return a pointer to the found element/nullptr
, not an index.] So I do not want to have to re-write the code in each of these two methods. How can I write it only once? I'm going to have trouble if my non-const
overload tries to call theconst
overload, or if theconst
overload tries to call the non-const
one, either way, aren't I? -
@jsulm
But I know this, which is why I wroteso I don't want to return
const int *
, but this method itself does not alter memberI was asking (nicely) if you have a reference to this aspect of
const
method in C++ I could read up on? I do understand aconst
method cannot alter a member, but in itself this method does not do so.Let me try this: sometimes I use this return result just for read, sometimes I want to write back into the pointed-to. Obviously if I just remove the
const
off the method, I cannot call it in the read-only case from some other classconst
method.How to achieve? Do I have to provide two methods:
int *pointerToMember() { return &member; } const int *pointerToMember() const { return &member; }
I have a feeling you may answer "yes" to that?
Supposing you do: assume the body of the method is several lines long. [For the record, it's actually a look-up in a member array of
struct
s, and I want to return a pointer to the found element/nullptr
, not an index.] So I do not want to have to re-write the code in each of these two methods. How can I write it only once? I'm going to have trouble if my non-const
overload tries to call theconst
overload, or if theconst
overload tries to call the non-const
one, either way, aren't I?@JonB said in Return pointer-to-member in const method:
How can I write it only once?
You can call the const version in non-const version and const_cast the return value from the const version. const_cast should of course be avoided, but in your scenario I think it is OK.
const int *pointerToMemberConst() const { return &member; } int *pointerToMember() { return const_cast<int*>(pointerToMemberConst()); }
I don't have a good source for this topic at hand.
-
@JonB said in Return pointer-to-member in const method:
How can I write it only once?
You can call the const version in non-const version and const_cast the return value from the const version. const_cast should of course be avoided, but in your scenario I think it is OK.
const int *pointerToMemberConst() const { return &member; } int *pointerToMember() { return const_cast<int*>(pointerToMemberConst()); }
I don't have a good source for this topic at hand.
@jsulm
Yes, and thank you, that was all I could think of doing myself, that way round. It's just that borrowing from @Chris-Kawa over at https://forum.qt.io/topic/120328/std-vector-qvector-and-fields/11, hisand a cute fluffy kitten will gruesomely die somewhere every time you do that :(
applies to (de-)const-casting ;-)
-
@jsulm
Yes, and thank you, that was all I could think of doing myself, that way round. It's just that borrowing from @Chris-Kawa over at https://forum.qt.io/topic/120328/std-vector-qvector-and-fields/11, hisand a cute fluffy kitten will gruesomely die somewhere every time you do that :(
applies to (de-)const-casting ;-)
-
@jsulm
OK, we're nearly there, but we have a wrinkle.I want these two methods to have the same name. So I don't want the
Const
suffix on theconst
one.I am assuming we can do this in C++, because Qt has methods like
QVector
:T & operator[](int i) const T & operator[](int i) const
(I know that's an operator, but I think if I looked around there are other methods which have both
const
& non-const
variants?)So I try:
const int *pointerToMember() const { return &member; } int *pointerToMember() { return const_cast<int*>(pointerToMember()); }
But the second overload gives Creator yellow-triangle clang warning
warning: all paths through this function will call itself
gcc compiles without warning. But when I test at run-time, sure enough, I get infinite recursion: the second overload calls itself, not the first one, and I am foo-barred :(
So.... Qt seems to manage with same-named
const
& non-const
variants. How can I adapt your suggestion, so they use common code yet work as required, please? E.g. is there anything I can write here to make the second overload'spointerToMember()
call the first one, not itself, some "overloadOf
" or something?EDIT
I guess I could do:private: const int *_pointerToMember() const { return &member; } public: const int *pointerToMember() const { return _pointerToMember(); } int *pointerToMember() { return const_cast<int*>(_pointerToMember()); }
Can I do it without that extra dummy method? Neater, and guaranteed [don't start on me with
inline
;-) ] not to have an inefficient extra function call :) -
@jsulm
OK, we're nearly there, but we have a wrinkle.I want these two methods to have the same name. So I don't want the
Const
suffix on theconst
one.I am assuming we can do this in C++, because Qt has methods like
QVector
:T & operator[](int i) const T & operator[](int i) const
(I know that's an operator, but I think if I looked around there are other methods which have both
const
& non-const
variants?)So I try:
const int *pointerToMember() const { return &member; } int *pointerToMember() { return const_cast<int*>(pointerToMember()); }
But the second overload gives Creator yellow-triangle clang warning
warning: all paths through this function will call itself
gcc compiles without warning. But when I test at run-time, sure enough, I get infinite recursion: the second overload calls itself, not the first one, and I am foo-barred :(
So.... Qt seems to manage with same-named
const
& non-const
variants. How can I adapt your suggestion, so they use common code yet work as required, please? E.g. is there anything I can write here to make the second overload'spointerToMember()
call the first one, not itself, some "overloadOf
" or something?EDIT
I guess I could do:private: const int *_pointerToMember() const { return &member; } public: const int *pointerToMember() const { return _pointerToMember(); } int *pointerToMember() { return const_cast<int*>(_pointerToMember()); }
Can I do it without that extra dummy method? Neater, and guaranteed [don't start on me with
inline
;-) ] not to have an inefficient extra function call :)@JonB It's getting ugly :-)
const int *pointerToMember() const { return &member; } int *pointerToMember() { const MyClass *_this = this; return const_cast<int*>(_this->pointerToMember()); } // Now compiler knows that you want to call const pointerToMember
-
@JonB It's getting ugly :-)
const int *pointerToMember() const { return &member; } int *pointerToMember() { const MyClass *_this = this; return const_cast<int*>(_this->pointerToMember()); } // Now compiler knows that you want to call const pointerToMember
@jsulm
Hmm, good one, thanks! Now I have an extra assignment to slow my code down :( ;-)This is indeed fine, and I get it. Nonetheless, final question: is there something in C++ to do with like "overload of" which can pick between the
const
& non-const
variants here? Just because I am interested. You know that when we use Qtconnect()
we can useQOverload<...>::of
to pick desired function overloads, I have a feeling that resolves to some C++ "overload of" statement? But here we are trying to distinguish between signatures which do not differ in parameters, I'm trying to understand whether that can be done? -
const auto value1 = obj->pointerToMember(); // should be const version auto value2 = obj->pointerToMember(); // should be non-const version
const int *pointerToMember() const { ... } int *pointerToMember() { ... } const auto value1 = obj->pointerToMember(); // should be const version
Nope (but thanks for your interest!), that
const
one steps straight into the second, non-const
variant! (As does the other call too.)I would not expect
const variable = method()
to call theconst
variant of the method, particularly. So far as I know, thatconst
on the variable perfectly allows it to pick any non-const
method, it only chooses to treat the return result asconst
but no other effect. Which is what it does it here. -
@JonB It's getting ugly :-)
const int *pointerToMember() const { return &member; } int *pointerToMember() { const MyClass *_this = this; return const_cast<int*>(_this->pointerToMember()); } // Now compiler knows that you want to call const pointerToMember
@jsulm said in Return pointer-to-member in const method:
int *pointerToMember() { const MyClass *_this = this; return const_cast<int*>(_this->pointerToMember()); } // Now compiler knows that you want to call const pointerToMember
I wrote
Now I have an extra assignment to slow my code down :( ;-)
So I adapted, to remove
_this
:int *pointerToMember() { return const_cast<int*>( const_cast<const MyClass *>(this)->pointerToMember() ); }
I prefer yours for readability :)
-
I left you for one minute and there's const_cast and goto :P
Getting back to original problem. At first glance you could do something like this:
class Foo { private: std::array<Stuff, 42> data; int indexOf(Key key) const { return /* some lengthy way to determine the index */; }; public: Stuff* get(Key key) { int index = indexOf(key); return (index < 0) ? nullptr : &data[index]; } const Stuff* get(Key key) const { int index = indexOf(key); return (index < 0) ? nullptr : &data[index]; } };
no casts but I would argue that this is a patch work. The solution becomes a lot nicer if you dig into the problem, which is you're trying to pack two things into one getter - a logic to determine if given element is present and retrieving it. Those are two things and they also incur a performance cost (branching) so I'd say design your interface so that the decision about taking the cost or not is left to the user of your class:
class Bar { private: std::array<Stuff, 42> data; public: int indexOf(Key key) const { return /* some lengthy way to determine the index */; }; Stuff& at(int index) { return data[index]; } const Stuff& at(int index) const { return data[index]; } };
Shorter, easier, faster and class doesn't absorb responsibilities it doesn't need to. Also those
at
methods will most definitely get inlined and disappear (inlining is real and super important, don't dismiss it :) )As for the other thing:
const auto value1 = obj->pointerToMember(); // should be const version
auto value2 = obj->pointerToMember(); // should be non-const versionconst or non-const is determined by the constness of the object it is called on, not unrelated variable the result is assigned to, so:
auto value1 = const_obj->pointerToMember(); // const version, auto resolves to const something auto value2 = obj->pointerToMember(); // non-const version, auto resolves to non-const something
Btw. this is a source of a common performance trap with Qt and C++11:
QVector<Stuff> stuff; for (Stuff& foo : stuff) {} //no! calls non-const begin/end and can cause expensive detach()
so people think "oh, I should just add const and it's fine":
for (const Stuff& foo : stuff) {} //no! still calls non-const begin/end
The proper way to do it is:
for (const Stuff& foo : qAsConst(stuff)) {} //ok, calls const begin/end
-
I left you for one minute and there's const_cast and goto :P
Getting back to original problem. At first glance you could do something like this:
class Foo { private: std::array<Stuff, 42> data; int indexOf(Key key) const { return /* some lengthy way to determine the index */; }; public: Stuff* get(Key key) { int index = indexOf(key); return (index < 0) ? nullptr : &data[index]; } const Stuff* get(Key key) const { int index = indexOf(key); return (index < 0) ? nullptr : &data[index]; } };
no casts but I would argue that this is a patch work. The solution becomes a lot nicer if you dig into the problem, which is you're trying to pack two things into one getter - a logic to determine if given element is present and retrieving it. Those are two things and they also incur a performance cost (branching) so I'd say design your interface so that the decision about taking the cost or not is left to the user of your class:
class Bar { private: std::array<Stuff, 42> data; public: int indexOf(Key key) const { return /* some lengthy way to determine the index */; }; Stuff& at(int index) { return data[index]; } const Stuff& at(int index) const { return data[index]; } };
Shorter, easier, faster and class doesn't absorb responsibilities it doesn't need to. Also those
at
methods will most definitely get inlined and disappear (inlining is real and super important, don't dismiss it :) )As for the other thing:
const auto value1 = obj->pointerToMember(); // should be const version
auto value2 = obj->pointerToMember(); // should be non-const versionconst or non-const is determined by the constness of the object it is called on, not unrelated variable the result is assigned to, so:
auto value1 = const_obj->pointerToMember(); // const version, auto resolves to const something auto value2 = obj->pointerToMember(); // non-const version, auto resolves to non-const something
Btw. this is a source of a common performance trap with Qt and C++11:
QVector<Stuff> stuff; for (Stuff& foo : stuff) {} //no! calls non-const begin/end and can cause expensive detach()
so people think "oh, I should just add const and it's fine":
for (const Stuff& foo : stuff) {} //no! still calls non-const begin/end
The proper way to do it is:
for (const Stuff& foo : qAsConst(stuff)) {} //ok, calls const begin/end
@Chris-Kawa said in Return pointer-to-member in const method:
I left you for one minute and there's const_cast and goto :P
That's what happens to fluffy kittens if you turn your back....
Let's pick my one of what (I understand) you suggest:
int indexOf(Key key) const { return /* some lengthy way to determine the index */; }; Stuff& at(int index) { return data[index]; } const Stuff& at(int index) const { return data[index]; }
So, you avoid the problem by making the lookup function return an
int index
. Then you return const/non-constdata[index]
.This breaks my (unspoken) "efficiency" criterion! My lookup marches through the array without an int counter, only with a pointer (it can return
nullptr
on not-found, so no references here), and returns that. You will make me turn that into an index, and then you will turn it back by adding it ontodata
.I am shocked. I was speaking to someone recently in another thread here, and they berated me for the overhead of indexing into arrays, when I said it was "milliseconds" they said "every microsecond counts, in game development, this is not to be ignored". Can you think who that was? :D
-
@Chris-Kawa said in Return pointer-to-member in const method:
I left you for one minute and there's const_cast and goto :P
That's what happens to fluffy kittens if you turn your back....
Let's pick my one of what (I understand) you suggest:
int indexOf(Key key) const { return /* some lengthy way to determine the index */; }; Stuff& at(int index) { return data[index]; } const Stuff& at(int index) const { return data[index]; }
So, you avoid the problem by making the lookup function return an
int index
. Then you return const/non-constdata[index]
.This breaks my (unspoken) "efficiency" criterion! My lookup marches through the array without an int counter, only with a pointer (it can return
nullptr
on not-found, so no references here), and returns that. You will make me turn that into an index, and then you will turn it back by adding it ontodata
.I am shocked. I was speaking to someone recently in another thread here, and they berated me for the overhead of indexing into arrays, when I said it was "milliseconds" they said "every microsecond counts, in game development, this is not to be ignored". Can you think who that was? :D
@JonB said:
Can you think who that was? :D
Yup, and I stand by what I said, which was not indexing into an array but indexing into a vector. Huge huge difference.
The int index is just an example. Every case is different and it doesn't have to be an int. It could be an iterator, some hash thingie or whatever is most efficient in your case. Pointer has that nasty proprty of being both index and data at the same time, which causes your problems - you want a const pointy thing and a non-const data thing. One variable can't be both at the same time so that's why I'm suggesting to split them up.
-
@JonB said:
Can you think who that was? :D
Yup, and I stand by what I said, which was not indexing into an array but indexing into a vector. Huge huge difference.
The int index is just an example. Every case is different and it doesn't have to be an int. It could be an iterator, some hash thingie or whatever is most efficient in your case. Pointer has that nasty proprty of being both index and data at the same time, which causes your problems - you want a const pointy thing and a non-const data thing. One variable can't be both at the same time so that's why I'm suggesting to split them up.
int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
The problem here is, that function returning
int
does not exhibit the problem! You don't have to worry about returningint
versusconst int
. It's when function returns asomething *
.int method() const
is never a problem, butint *method() const
where it returns a member variable is a problem. So I see I then needconst int *method() const
as well asint *method()
. Once yourindexOf()
returns a pointer into a member variable thingie you end up needing aconst something *indexOf() const
and asomething *indexOf(Key key)
, for a method which does the same thing.Anyways. My head is hurting on this. We may all be saying the same thing in different ways.
-
int indexOf(Key key) const { return /* some lengthy way to determine the index */; };
The problem here is, that function returning
int
does not exhibit the problem! You don't have to worry about returningint
versusconst int
. It's when function returns asomething *
.int method() const
is never a problem, butint *method() const
where it returns a member variable is a problem. So I see I then needconst int *method() const
as well asint *method()
. Once yourindexOf()
returns a pointer into a member variable thingie you end up needing aconst something *indexOf() const
and asomething *indexOf(Key key)
, for a method which does the same thing.Anyways. My head is hurting on this. We may all be saying the same thing in different ways.
@JonB said in Return pointer-to-member in const method:
The problem here is, that function returning int does not exhibit the problem!
Exactly, it's intentional on my part and that's the point. Don't try to solve an ugly problem. Redesign and untangle dependencies so there is no problem in the first place ;)
int*
is an "index" andint*
points to data. Untangle those roles.Anyways. My head is hurting on this.
I fear I might have accidentally terrorized you into being paranoid about something that will bring you marginal gains and make your code a lot worse to read/maintain. I'd say, just for test, do the duplicated const and non-const methods, measure how much gain are you actually getting, decide if it's worth it and only then proceed or revert. While I care deeply about performance there is a line below which it's just not worth it, as in how much optimizations can you achieve in a finite amount of time and how it reflects on readability and ease of maintenance. I'd just like that line to be lower than it usually is, but it's up to you really. Don't let me pressure you too much. I've been told I can be bossy ;)
-
@JonB said in Return pointer-to-member in const method:
The problem here is, that function returning int does not exhibit the problem!
Exactly, it's intentional on my part and that's the point. Don't try to solve an ugly problem. Redesign and untangle dependencies so there is no problem in the first place ;)
int*
is an "index" andint*
points to data. Untangle those roles.Anyways. My head is hurting on this.
I fear I might have accidentally terrorized you into being paranoid about something that will bring you marginal gains and make your code a lot worse to read/maintain. I'd say, just for test, do the duplicated const and non-const methods, measure how much gain are you actually getting, decide if it's worth it and only then proceed or revert. While I care deeply about performance there is a line below which it's just not worth it, as in how much optimizations can you achieve in a finite amount of time and how it reflects on readability and ease of maintenance. I'd just like that line to be lower than it usually is, but it's up to you really. Don't let me pressure you too much. I've been told I can be bossy ;)
@Chris-Kawa said in Return pointer-to-member in const method:
measure how much gain are you actually getting, decide if it's worth it and only then proceed or revert
Sounds like what I would say :)
While I care deeply about performance
I do, but kinda more just in an algorithmic sense than whether it makes any visible difference to what I'm doing.
Don't let me pressure you too much. I've been told I can be bossy ;)
Not at all! I read your posts with interest, high quality.
This has all revealed something to me which I had not appreciated. I thought
Class::method() const
only guaranteed that it did not alter*this
. I did not expect that, for safety, it also does not allowClass::Member *Class::method() const
. That function does not itself alter*this
, but I do see that it returns a write-pointer intoconst this *
which could later be used to do so. Hence you have to make that return aconst *
if you wantmethod() const
I just was not aware of this.I'm sure there are just pages of C++ specs I could/ought to read up on
const
.... [Actually I think I did so a while ago, I recall it being longggggg.]P.S.
When I started C it didn't haveconst
yet. Lambs gambolled carefree in the fields, life was easy then... -
If you want some further discussion points, take a look at how the Qt api returns pointers:
QLayoutItem *QGridLayout::itemAtPosition(int row, int column) const
QUndoStack *QUndoGroup::activeStack() const
QObject *QDropEvent::source() constwant more? :)