From: Martin B. on
DeMarcus wrote:
> Hi,
> I have some trouble understanding the true meaning of const.
> Please consider this example.
>
> class Store
> {
> public:
> void add( int* item ) { /* ... */ }
> };
>
> class Item
> {
> public:
> Item( int item ) : item_(item) {}
> void giveToStore( Store& store ) const { store.add( &item_ ); }
>
> private:
> int item_;
> };
>
> Now, this gives me "invalid conversion from 'const int*' to 'int*'" when
> exposing item_ in store.add( &item_ ). What's the way of thinking here?
>(....)
> 4. Redesign according to C++ Coding Standards by Sutter & Alexandrescu,
> Item 11 - Hide information. Basically it says; "Don't expose internal
> information from an entity that provides an abstraction.". However, my
> real problem (illustrated with Item) is a kind of wrapper similar to
> boost::shared_ptr and I need something like boost::shared_ptr::get().
> Therefore I'm clueless about finding an alternative design.
>

A const member function must not only not modify the object, it must
also not expose anything that could be used to directly modify the object.

With shared_ptr, get() can return the non-const pointer since shared_ptr
holds the pointer, not the object itself. (Plus one might note that if
you use that pointer to do a delete, things come crashing down.)

With your example, modifying the item pointer to item_ always results in
modifying the Item object, so you either give out a const pointer or the
member function is indeed not const. _If_ giveToStore should be const,
then Store::add should take a const int* -- _if_ Store is allowed to
modify item, the giveToStore should not be const.

br,
Martin

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: DeMarcus on
Martin B. wrote:
> DeMarcus wrote:
>> Hi,
>> I have some trouble understanding the true meaning of const.
>> Please consider this example.
>>
>> class Store
>> {
>> public:
>> void add( int* item ) { /* ... */ }
>> };
>>
>> class Item
>> {
>> public:
>> Item( int item ) : item_(item) {}
>> void giveToStore( Store& store ) const { store.add( &item_ ); }
>>
>> private:
>> int item_;
>> };
>>
>> Now, this gives me "invalid conversion from 'const int*' to 'int*'" when
>> exposing item_ in store.add( &item_ ). What's the way of thinking here?
>> (....)
>> 4. Redesign according to C++ Coding Standards by Sutter & Alexandrescu,
>> Item 11 - Hide information. Basically it says; "Don't expose internal
>> information from an entity that provides an abstraction.". However, my
>> real problem (illustrated with Item) is a kind of wrapper similar to
>> boost::shared_ptr and I need something like boost::shared_ptr::get().
>> Therefore I'm clueless about finding an alternative design.
>>
>
> A const member function must not only not modify the object, it must
> also not expose anything that could be used to directly modify the object.
>
> With shared_ptr, get() can return the non-const pointer since shared_ptr
> holds the pointer, not the object itself. (Plus one might note that if
> you use that pointer to do a delete, things come crashing down.)
>
> With your example, modifying the item pointer to item_ always results in
> modifying the Item object, so you either give out a const pointer or the
> member function is indeed not const. _If_ giveToStore should be const,
> then Store::add should take a const int* -- _if_ Store is allowed to
> modify item, the giveToStore should not be const.
>
> br,
> Martin
>

Thanks all of you for great input! Here's a more detailed version of my
structure.

class NumberCruncher // Previously Store, and it may change values
{
public:
template<typename T>
void addItem( T* item ) { /* Do number crunching on item */ }
};

class ItemContainerInterface // Previously just Item
{
public:
// This is the const in question.
virtual addMe( NumberCruncher& ) const = 0;
virtual removeMe( NumberCruncher& ) const = 0;
};

template<typename T>
class ItemContainer : public ItemContainerInterface
{
public:
virtual addMe( NumberCruncher& nc ) const
{ nc.addItem( &item_ ); }

virtual removeMe( NumberCruncher& nc ) const
{ nc.removeItem( &item_ ); }
private:
T item_;
};

template<typename T>
class ItemSharedPtrContainer : public ItemContainerInterface
{
public:
virtual addMe( NumberCruncher& nc ) const
{ nc.addItem( item_.get() ); }
virtual removeMe( NumberCruncher& nc ) const
{ nc.removeItem( &item_.get() ); }
private:
boost::shared_ptr<T> item_;
};

class DataSet
{
public:
void addAllToNC( NumberCruncher& nc ) const
{
auto end = dataSet_.end();
for( auto i = dataSet_.begin; i != end; ++i )
(*i)->addMe( nc );
}
private:
std::vector<ItemContainerInterface*> dataSet_;
};

int main()
{
NumberCruncher nc;
DataSet d;
int anInt = 63;
boost::shared_ptr<Circle> sp( new Circle( 12 ) );
d.addSomeItem( 35 );
d.addSomeItem( sp );
d.addSomeItem( 47.11 );
d.addSomeItem( boost::ref( anInt ) );

d.addAllToNC( nc );
}

I've left out some functions here and there but the general message is
that the previous Store (now NumberCruncher) can alter the data in the
pointer it gets. Not directly with addItem() but at a later time.

The problem is that the item container can hold both shared pointers and
the type itself. So sometimes it's an ItemContainer and sometimes it's
an ItemValue. In both cases it is a kind of owner of the value, so maybe
I have to remove const from ItemContainerInterface::addMe() because the
value may be changed at a later time by NumberCruncher.

The difficulty is that ItemSharedPtrContainer only holds a pointer and
therefore addMe() could be const. But ItemContainer holds the value and
therefore it cannot be const. Shall I just remove const to support both
containers or is there a way to refactor the design?

Thanks!

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Martin B. on
On 19.02.2010 23:34, DeMarcus wrote:
> Martin B. wrote:
>> DeMarcus wrote:
>>> Hi,
>>> I have some trouble understanding the true meaning of const.
>>> Please consider this example.
>>>(....)
>>> Now, this gives me "invalid conversion from 'const int*' to 'int*'" when
>>> exposing item_ in store.add( &item_ ). What's the way of thinking here?
>>> (....)
>>> 4. Redesign according to C++ Coding Standards by Sutter & Alexandrescu,
>>> Item 11 - Hide information. Basically it says; "Don't expose internal
>>> information from an entity that provides an abstraction.". However, my
>>> real problem (illustrated with Item) is a kind of wrapper similar to
>>> boost::shared_ptr and I need something like boost::shared_ptr::get().
>>> Therefore I'm clueless about finding an alternative design.
>>>
>>
>> A const member function must not only not modify the object, it must
>> also not expose anything that could be used to directly modify the
>> object.
>>(....)
>
> Thanks all of you for great input! Here's a more detailed version of my
> structure.
>
> class NumberCruncher // Previously Store, and it may change values
> {
> public:
> template<typename T>
> void addItem( T* item ) { /* Do number crunching on item */ }
> };
>
> class ItemContainerInterface // Previously just Item
> {
> public:
> // This is the const in question.
> virtual addMe( NumberCruncher& ) const = 0;
> virtual removeMe( NumberCruncher& ) const = 0;
> };
>
> template<typename T>
> class ItemContainer : public ItemContainerInterface
> {
> public:
> virtual addMe( NumberCruncher& nc ) const
> { nc.addItem( &item_ ); }
>
> virtual removeMe( NumberCruncher& nc ) const
> { nc.removeItem( &item_ ); }
> private:
> T item_;
> };
>
> template<typename T>
> class ItemSharedPtrContainer : public ItemContainerInterface
> {
> public:
> virtual addMe( NumberCruncher& nc ) const
> { nc.addItem( item_.get() ); }
> virtual removeMe( NumberCruncher& nc ) const
> { nc.removeItem( &item_.get() ); }
> private:
> boost::shared_ptr<T> item_;
> };
>(....)
>
> I've left out some functions here and there but the general message is
> that the previous Store (now NumberCruncher) can alter the data in the
> pointer it gets. Not directly with addItem() but at a later time.
>
> The problem is that the item container can hold both shared pointers and
> the type itself. So sometimes it's an ItemContainer and sometimes it's
> an ItemValue. In both cases it is a kind of owner of the value, so maybe
> I have to remove const from ItemContainerInterface::addMe() because the
> value may be changed at a later time by NumberCruncher.
>
> The difficulty is that ItemSharedPtrContainer only holds a pointer and
> therefore addMe() could be const. But ItemContainer holds the value and
> therefore it cannot be const. Shall I just remove const to support both
> containers or is there a way to refactor the design?
>

The question is a rather difficult one and I'm not sure if a general
answer can be given, but:
You call it "ItemContainerInterface" - look at the std::containers
(vector etc.). These use const memberfunctions to return const
references and non-const member function to give access to modifyable items.
You also have to ask yourself the question if you want to be able to
call addMe() on a _const_ ItemContainerInterface. I'm not sure this
would make sense, so dropping the const might be a good idea.
If the constness is really something you want of the addMe() function
with regard to the ItemContainerInterface, then I would make the item_
member of ItemContainer mutable.

br,
Martin

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Bart van Ingen Schenau on
On Feb 19, 11:34 pm, DeMarcus <use_my_alias_h...(a)hotmail.com> wrote:
>
> I've left out some functions here and there but the general message is
> that the previous Store (now NumberCruncher) can alter the data in the
> pointer it gets. Not directly with addItem() but at a later time.

Here you give the answer to your later question: As addItem() can
cause the data in the pointer to be altered, you don't want addMe() to
be callable for a (logically) immutable ItemContainer.
To ensure that such an erronous call is not possible, addMe() should
be declared non-const.

>
> The problem is that the item container can hold both shared pointers and
> the type itself. So sometimes it's an ItemContainer and sometimes it's
> an ItemValue. In both cases it is a kind of owner of the value, so maybe
> I have to remove const from ItemContainerInterface::addMe() because the
> value may be changed at a later time by NumberCruncher.
>
> The difficulty is that ItemSharedPtrContainer only holds a pointer and
> therefore addMe() could be const. But ItemContainer holds the value and
> therefore it cannot be const. Shall I just remove const to support both
> containers or is there a way to refactor the design?

If you are adding const to member-functions just because you can
without complaint from the compiler, then you are going down the wrong
way.
You should only make member-functions const if it makes sense to use
that member-function on an immutable object. If it does not make sense
(like in your addMe() case), then you should meke the member-function
non-const.

>
> Thanks!
>

Bart v Ingen Schenau


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Dave Harris on
use_my_alias_here(a)hotmail.com (DeMarcus) wrote (abridged):
> However, my real problem (illustrated with Item) is a kind of
> wrapper similar to boost::shared_ptr and I need something like
> boost::shared_ptr::get().

Boost::shared_ptr is perhaps not the best example to follow here. To
explain why I need some background.

The core issue is that pointers can be used in two ways: they may mean
the pointer and pointee are parts of the same logical object, or they may
mean the pointer merely uses the pointee. In the former case, if the
pointer is const then the pointee should be too. In the later case, the
constness of the pointer is unrelated to the constness of the pointee.

There is only one kind of built-in C/C++ pointer, so it is used to
express both kinds of relationship. This means it has to leave constness
unrelated. Boost designed share_ptr to mimic built-in pointers, so it
does the same. In practice, it is often not what you want. Indeed, it's
surprising that code like:

class Demo {
int *p;
void test() const {
++*p;
}
};

compiles despite the const. Arguably boost should have added two flavours
of shared_ptr, with the default being that const is propagated:

class Demo2 {
shared_ptr<int> p;
void test() const {
++*p; // Should fail to compile.
}
};

because this is the safer default (and by far the most common, in my
experience). Had they done this, you would not have been mislead when you
copied them.

-- Dave Harris, Nottingham, UK.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]