The following compiles without error on four different platforms (Linux
g++, Sun CC, HP aCC, Win Dev-C++) so I suspect it's ok, but I don't see
why this isn't a const-related error. pB is a pointer to constant B and
foo() is a non-constant function invoked on a (reference) member of B.
Can someone explain this?
Thanks,
Mark
struct A
{
void foo () {}
};
struct B
{
B () : a(*new A) {}
A& a;
};
struct C
{
C () : pB(new B) {}
void bar () {pB->a.foo();}
const B* pB;
};
int main ()
{
C c;
c.bar();
} 7 1872
Mark P wrote: The following compiles without error on four different platforms (Linux g++, Sun CC, HP aCC, Win Dev-C++) so I suspect it's ok, but I don't see why this isn't a const-related error. pB is a pointer to constant B and foo() is a non-constant function invoked on a (reference) member of B. Can someone explain this?
Thanks, Mark
struct A { void foo () {} };
OK, 'foo' is a non-const member, no mystery here. I will not be
called for an object of type 'const A'. But it can be called for
any object of type 'A'.
struct B { B () : a(*new A) {} A& a;
'a' is a _reference_ to a _non-const_ object. IOW, 'a' refers to
an object of type 'A', not to an object of type 'const A'. The
const-ness of the object that contains this reference does not
matter here. Even if 'B' here is a 'const B', 'a' will still
refer to a non-const 'A'.
};
struct C { C () : pB(new B) {} void bar () {pB->a.foo();}
const B* pB;
pB here points to a 'const B'. OK. When you try to access any
member of '*pB' object, the member is going to be 'const', but
that's only the member _itself_. In the case of 'a', what's
const here is the reference, which is irrelevant really because
references themselves cannot be changed. The _referred_ object,
however, is not 'const', although technically 'a' is. What you
actually have (if it were possible in C++) is:
A & const a;
(a constant reference that refers to a non-constant object).
It would be a bit clearer if you had 'a' of type 'A*'. If the
'B' in which 'a' lived would be const, then the type of its 'a'
would be {A* const}, and not {A const *}, which would still allow
changing the pointed object (and of course call a non-const 'foo'
member).
};
int main () { C c; c.bar(); }
V
--
Please remove capital As from my address when replying by mail
Mark P wrote: The following compiles without error on four different platforms (Linux g++, Sun CC, HP aCC, Win Dev-C++) so I suspect it's ok, but I don't see why this isn't a const-related error. pB is a pointer to constant B and foo() is a non-constant function invoked on a (reference) member of B. Can someone explain this?
B is constant. The A it references is not.
Make the B member a pointer to A instead of a reference for a very
similar situation that will be easier to understand.
The pointer (member of B) would be const. But the A it points to would not.
The fact it's a reference does not change this. Except
that you would not be able to modify the B member EVEN IF the B was not
constant (because you cannot rebind references).
In other words: a reference member of a const object is a constant
reference, not a reference to a constant something. And this is true
even if the language would not let you modify a non costant reference to
either constant or non constant whatever.
HTH
AnalogFile wrote: Mark P wrote: The following compiles without error on four different platforms (Linux g++, Sun CC, HP aCC, Win Dev-C++) so I suspect it's ok, but I don't see why this isn't a const-related error. pB is a pointer to constant B and foo() is a non-constant function invoked on a (reference) member of B. Can someone explain this?
B is constant. The A it references is not.
Make the B member a pointer to A instead of a reference for a very similar situation that will be easier to understand.
The pointer (member of B) would be const. But the A it points to would not.
The fact it's a reference does not change this. Except that you would not be able to modify the B member EVEN IF the B was not constant (because you cannot rebind references).
In other words: a reference member of a const object is a constant reference, not a reference to a constant something. And this is true even if the language would not let you modify a non costant reference to either constant or non constant whatever.
HTH
Thanks to both you and Victor. That clears it up very well.
Mark P wrote: AnalogFile wrote: Mark P wrote: The following compiles without error on four different platforms (Linux g++, Sun CC, HP aCC, Win Dev-C++) so I suspect it's ok, but I don't see why this isn't a const-related error. pB is a pointer to constant B and foo() is a non-constant function invoked on a (reference) member of B. Can someone explain this? B is constant. The A it references is not.
.... Thanks to both you and Victor. That clears it up very well.
One more thing (with apologies to Steve Jobs),
Note: what follows is probably basic on comp.lang.c++, but the thread is
crossposted with alt.comp.lang.l earn.c-c++ and I'm leaving the former
in, for now, for thread completeness. If this will spawn a longer
subthread we may want to break out and stop crossposting.
I've been thinking why you would want the A to be constant too and if
this made sense, and how you would actually do it if you need A to be
constant when seen throu a constant B.
Consider:
class A {
public:
virtual void mutate();
virtual ~A()=0;
static A* New( /* some params */ );
protected:
A();
A(const A&);
A& operator=(const A&);
};
The above is somewhat "classic". You probably recognize some patterns.
A is the (abstract) base of a hidden hierarchy. You need not know the
actual implementation classes: you instantiate only through A::New. And
that gives you a dynamically allocated instance of some class derived
from A.
Now for B:
// we have an A, and it's "value" is part of our "value"
class B {
public:
A a; // error
};
There are many possible reasons to have an A bound to a B. Here a B "has
an" A as part of its 'value' (or 'state' or whatever). But it does not
work because we can only have dynamic As. Therefore we go with something
like you did in your OP:
class B {
public:
A &a;
B()
:a( * A::New(/*params*/) )
{
}
~B() { delete &a; }
};
I hope the reason you used the *ugly* initializer '*new A' is not this
one but just a way to represent your code without a lot of details and
you actually have the reference initialized from a parameter or
something. But I'll do the same: I'm just making a completely different
point and the above code will go away in a moment.
Now the above works. Or does it?
We have the exact problem you asked about :
void foo( const B& b )
{
// this violates contract
b.a.mutate();
}
As the A is part of the "value" of B, you are changing the "value" of a
supposedly constant object.
How do we solve this?
That is: how do you make sure the A is also seen as constant if the B is
such?
This is were function overloading comes to the game. And the reason
accessors are much superior to exposed data members:
class B {
A *pa;
public:
B()
:pa( A::New(/*params*/) )
{
}
~B() { delete &a; }
A& a() { return *pa; }
const A& a() const { return *pa; }
};
Sure you now need an extra pair of character to access the A, so the
interface is not exactly the same.
But this is how it should have been from day one.
Not sure if and how the above applies to your program, but it is closely
related to the kind of surprise you had in the OP.
AnalogFile <ne**@NOSPAMkuv a.itREALLYNO> wrote: Consider:
class A { public: virtual void mutate(); virtual ~A()=0; static A* New( /* some params */ ); protected: A(); A(const A&); A& operator=(const A&); };
class B { A *pa; public: B() :pa( A::New(/*params*/) ) { }
~B() { delete &a; }
A& a() { return *pa; } const A& a() const { return *pa; } };
Sure you now need an extra pair of character to access the A, so the interface is not exactly the same.
Instead of providing those accessors, you also could have worked this
with a special kind of smart pointer. Obviously, it would have to be
special in a way that only class B is allowed to make the smart pointer
delete things. Instead of writing b.a ().foo; you would write
b.a->foo;
I guess it is a matter of preference. Though, imo, using the smart
pointer allows you to hide details of the construction of A and allows
for reuse.
regards
--
jb
(reply address in rot13, unscramble first)
Jakob Bieling wrote: AnalogFile <ne**@NOSPAMkuv a.itREALLYNO> wrote:
Consider:
class A { public: virtual void mutate(); virtual ~A()=0; static A* New( /* some params */ ); protected: A(); A(const A&); A& operator=(const A&); };
class B { A *pa; public: B() :pa( A::New(/*params*/) ) { }
~B() { delete &a; }
A& a() { return *pa; } const A& a() const { return *pa; } };
Sure you now need an extra pair of character to access the A, so the interface is not exactly the same.
Instead of providing those accessors, you also could have worked this with a special kind of smart pointer. Obviously, it would have to be special in a way that only class B is allowed to make the smart pointer delete things. Instead of writing b.a ().foo; you would write b.a->foo;
I guess it is a matter of preference. Though, imo, using the smart pointer allows you to hide details of the construction of A and allows for reuse.
IMO this is not something to be considered when designing B.
If A is part of the "value" of B, then B should expose A. It's ok that A
is supposed to be allocated dynamically while B gives a reference to an
A instead of a pointer because that's a commonly understood way to
differentiate simple access from ownership transfer.
If smart pointers were to be introduced, that should happen at the
design level of A. And A::New should just not return a plain pointer,
but a smart pointer instead.
But you probably are not really asking for a smart pointer. You are
asking for B::a() to become operator-> of a special B member that will
take the name a. Like this:
struct B {
class indirect {
friend class B;
A *pa;
indirect(A*p):p a(p){}
indirect();
indirect(const indirect&r);
~indirect() { delete pa; }
indirect& operator=(const indirect&);
public:
A* operator->() { return pa; }
const A* operator->() const { return pa; }
} a;
B()
:a( A::New(/*params*/) )
{
}
};
That's ok. It's just a more convoluted way to implement the same
solution: use function overloading to grab constness.
It has the extra trick that plays with operators to make the code more
C/C++ lookalike and less pascal/java/python/ruby/whatever looking.
If you find this more legible and self documenting than the B::a()
method, go ahead.
Mark P wrote: The following compiles without error on four different platforms (Linux g++, Sun CC, HP aCC, Win Dev-C++) so I suspect it's ok, but I don't see why this isn't a const-related error. pB is a pointer to constant B and foo() is a non-constant function invoked on a (reference) member of B. Can someone explain this?
struct A { void foo () {} };
struct B { B () : a(*new A) {} A& a; };
struct C { C () : pB(new B) {} void bar () {pB->a.foo();}
const B* pB; };
int main () { C c; c.bar(); }
As others have pointed out, the A& isn't const, so there is no violation
of const correctness according to the rules of the language.
This is always a somewhat tricky subject in C++, because from an OO
perspective, there are two common but very different interpretations of
pointer or reference members of a class.
One is a simple indirection, where the object containing the reference
needs to know how to find some other object, but there is no particular
relationship beyond that. This would be common in many data structures,
for example. Here, we usually wouldn't want any constancy applied to the
containing object to apply to its pointers and references as well.
The other interpretation is effectively aggregation, where the
containing object is probably responsible for allocating and releasing
the referenced object, and the latter is treated as a subobject of the
former. This is quite common when you're modelling concrete, real-world
entities using objects, but for some reason you don't want to use a
simple data member for the subobjects. In this case, any constancy
applied to the containing object would, ideally, apply implicitly to any
contained pointers and references, since the pseudo-subobjects should be
treated as constant if their owner is.
Of course, C++ can't tell the difference from the code you give it. As
usual in such situations, it presents the full range of options to the
programmer and trusts that he will not "abuse the privilege". If your
example classes are intended to represent the aggregation case, then you
are committing that abuse by allowing public access to a contained
reference. If you want to make the code safer in that situation, then
the usual idiom is to make the reference member private, and then
provide two overloaded public accessor functions, one const-qualified
and one not, with the former returning the equivalent const reference.
Hope that helps,
Chris This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics |
by: Mark |
last post by:
Hi,
I'm trying to use hash_map (gcc 3.2.2) with a std::string as the key. It
will compile if I use <map> but I get a bunch of template compile errors
when I change it to hash_map. Any suggestions? My program and the errors
are below...
#include <ext/hash_map>
#include <string>
|
by: Ben Ingram |
last post by:
Hi all,
I am writing a template matrix class in which the template parameters are
the number of rows and number of columns. There are a number of reasons
why this is an appropriate tradeoff for my particular application. One of the
advantages is that the _compiler_ can force inner matrix dimensions used in
multiplication to agree. A _complie-time_ error will be triggered if you
write A * B and the number of coluns in A does not equal the...
|
by: Aguilar, James |
last post by:
Hello all,
To begin, yes, this -is- a homework assignment. However, it is for my
Algorithms class, and my instructor has given us explicit permission to use
"expert" groups like newsgroups, so if that's your only reason not to help,
please do. Otherwise, I guess it's OK. But, just remember, I'm not asking
you to do my work for me, just to point out my error.
My problem is not with the algorithm itself (standard divide and conquer on...
|
by: Ruben Campos |
last post by:
Greetings.
Some time ago, I had writing a CVector <T, N> class, which implements an
algebraic vector of arbitrary both dimension and scalar type. First, I
defined the interface for the generic algebraic vector class. The
problem I encountered there was that algebraic vectors of some concrete
dimensions were susceptible to include some extra methods not included
in the most generic case. For example, 3-dimensional vectors include the...
|
by: Yang Zhang |
last post by:
Here is a program:
/////////////////////////////////////////////////
#include <iostream>
using namespace std ;
class A {
int a ;
A(const A& aA) {
a=aA.a ;
cout<<"copy constructor called!"<<endl ;
| |
by: Bart Goeman |
last post by:
Hi,
I have a question about how to put redundant information in data
structures, initialized at compile time. This is often necessary
for performance reasons and can't be done at run time (data
structures are read only)
Ideally one should be able to put the redundant
information there automatically so no mistakes are possible, but in a lot
of case I see no way how to do it.
|
by: Eric Lilja |
last post by:
>From a book, I know the following is true for the comparison operators:
An overloaded operator that is a class member is only considered
when the operator is used with a *left* operand that is an object
of that class.
And is that also the reason why if you use class member functions for
operators << and >you have to write:
someclass << cout;
someclass >cin;
?
|
by: Piotr Sawuk |
last post by:
What is wrong with the following? Why doesn't it compile?
template<int f, class me, typename ValType=int>
class Fm
{
protected:
ValType v;
public:
me& operator+=(const me& o) {v=((me*)this)->operator+(o); return *(me*)this;}
me& operator*=(const me& o) {v=((me*)this)->operator*(o); return *(me*)this;}
|
by: Tomás Ó hÉilidhe |
last post by:
I've been developing a C89 microcontroller application for a while
now and I've been testing its compilation using gcc. I've gotten zero
errors and zero warnings with gcc, but now that I've moved over to the
micrcontroller compiler I'm getting all sorts of errors.
One thing I'd like to clarify is the need (in C89) for a compile-
time constant in the initialiser of a variable. The compiler rejects the
following source file:
/* Start...
|
by: marktang |
last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look !
Part I. Meaning of...
|
by: Oralloy |
last post by:
Hello folks,
I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>".
The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed.
This is as boiled down as I can make it.
Here is my compilation command:
g++-12 -std=c++20 -Wnarrowing bit_field.cpp
Here is the code in...
| |
by: tracyyun |
last post by:
Dear forum friends,
With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
|
by: agi2029 |
last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own....
Now, this would greatly impact the work of software developers. The idea...
|
by: isladogs |
last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM).
In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules.
He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms.
Adolph will...
|
by: conductexam |
last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one.
At the time of converting from word file to html my equations which are in the word document file was convert into image.
Globals.ThisAddIn.Application.ActiveDocument.Select();...
|
by: TSSRALBI |
last post by:
Hello
I'm a network technician in training and I need your help.
I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs.
The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols.
I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
|
by: adsilva |
last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
| |
by: bsmnconsultancy |
last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...
| |