From: Martin B. on 18 Feb 2010 18:12 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 19 Feb 2010 05:34 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 20 Feb 2010 01:34 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 21 Feb 2010 21:34 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 24 Feb 2010 05:26 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! ]
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 4 Prev: UML Survey Next: Repeat a typedef or include a header with the typedef? |