Reputation: 1205
I am confused with the following code, where the class Baz
gives access to its internal data through the proxy class Bar
:
struct Foo{};
template<class T>
struct Bar
{
Bar(T &val_): val(val_) {}
T &val;
};
struct Baz
{
Bar<const Foo> get() const {return Bar<const Foo>(foo);}
Bar<Foo> get() {return Bar<Foo> (foo);}
Foo foo;
};
struct FooBaz
{
FooBaz(Baz &baz_): baz(baz_) {}
// Bar<const Foo> get() const {return baz.get();} // this do not compile
Bar<const Foo> get() const {return const_cast<const Baz &>(baz).get();} // the const_cast seems to be required
Baz &baz;
};
It seems that I must do a const_cast
to dispatch to the right Baz::get()
function.
The first thing that I don't understand is that it works if the FooBaz::baz
is a plain Baz
and not a reference:
struct Foo{};
template<class T>
struct Bar
{
Bar(T &val_): val(val_) {}
T &val;
};
struct Baz
{
Bar<const Foo> get() const {return Bar<const Foo>(foo);}
Bar<Foo> get() {return Bar<Foo> (foo);}
Foo foo;
};
struct FooBaz
{
FooBaz(Baz &baz_): baz(baz_) {}
Bar<const Foo> get() const {return baz.get();} // Ok
Baz baz;
};
What I understand about a const function member is that it makes the this
pointer-to-const, but in fact it is not totally clear to me what it means... Do all the data member be 'as if they are const'? Is there a clear reference to that?
Another interesting thing is that if I use a std::reference_wrapper
as proxy class, then it works without the const_cast
:
#include <functional>
struct Foo {};
struct Baz
{
std::reference_wrapper<const Foo> get() const {return std::cref(foo);}
std::reference_wrapper<Foo> get() {return std::ref (foo);}
Foo foo;
};
struct FooBaz
{
FooBaz(Baz &baz_): baz(baz_) {}
std::reference_wrapper<const Foo> get() const {return baz.get();} // Ok
Baz &baz;
};
What is the magic with the std::reference_wrapper
?
Thanks!
Upvotes: 0
Views: 247
Reputation: 172894
it works if the
FooBaz::baz
is a plainBaz
and not a reference:
That's the point. In const
member function, the data members are considered as const
too. Note that for reference, that means the reference member itself is considered as const
, not the object being referenced. i.e. baz
would be considered as Baz & const
(const reference) but not Baz const &
(reference to const). References can't be const-qualified in fact and the const-ness is just ignored, as the effect, even in const
member function, baz.get();
will always call the non-const Baz::get()
.
It doesn't change for the std::reference_wrapper
version, baz.get();
still calls the non-const Baz::get()
and returns std::reference_wrapper<Foo>
. std::reference_wrapper
has a converting constructor, the returned std::reference_wrapper<Foo>
could be converted to std::reference_wrapper<const Foo>
, which stores a reference to the object got from std::reference_wrapper<Foo>
(via the
conversion operator).
Upvotes: 3
Reputation: 96053
I must do a
const_cast
to dispatch to the rightBaz::get()
function.
Yes. Or std::as_const
, or make Bar<T>
convertible to Bar<const T>
.
What I understand about a const function member is that it makes the this pointer-to-const, but in fact it is not totally clear to me what it means... Do all the data member be 'as if they are const'?
Yes, the members behave as if they were const
.
The expression
E1->E2
is exactly equivalent to(*E1).E2
for built-in types; that is why the following rules address onlyE1.E2
.In the expression
E1.E2
:
if
E2
is a non-static data member:if
E2
is of reference typeT&
orT&&
, the result is an lvalue of typeT
designating the object or function to which E2 refers, otherwise, ifE1
is an lvalue, the result is an lvalue designating that non-static data member ofE1
, otherwise (ifE1
is an xvalue (which may be materialized from prvalue)), the result is an xvalue designating that non-static data member ofE1
.If
E2
is not a mutable member, the cv-qualification of the result is the union of the cv-qualifications ofE1
andE2
, otherwise (ifE2
is a mutable member), it is the union of the volatile-qualifications ofE1
andE2
;
When accessed through a pointer to const
class, its members become const
.
But the constness is only added at the top level. If you access int *member;
through a pointer to const
, it becomes int *const member;
, not const int *const member;
.
References work in a similar way, but since references themselves can't be const
1, their type is not changed.
if I use a
std::reference_wrapper
as proxy class, then it works without theconst_cast
That's because it a has a constructor that allows std::reference_wrapper<const T>
to be constructed from std::reference_wrapper<T>
.
1 Yes, a reference can't changed to point to a different object after it's created, but formally it's not const
. std::is_const
returns false
for references.
Upvotes: 1