I have the following problem and I wonder whether there's a better way to solve it:
class myObj {
public:
typedef std::shared_ptr<myObj> handle;
typedef std::shared_ptr<const myObj> const_handle;
int someMethod() { ... }
int someConstMethod() const { ... }
};
Now what I need is a container class that somehow allows you to modify or read a collection of myObj depending on its own constness, like so:
class myCollection {
public:
typedef std::list<myObj::handle> objList;
typedef std::list<myObj::const_handle> const_objList;
inline objList& modify() { return _obl; }
// it would be nice to do this, but it won't compile as
// objList and const_objList are completely different types
inline const_objList& read() const { return _obl; } // doh! compile error...
// returning a const objList won't help either as it would return non-const
// handles, obviously.
// so I am forced to do this, which sucks as i have to create a new list and copy
void read(const_objList &l) {
std::for_each(
_obl.begin(),
_obl.end(),
[&l] (myObj::handle &h) { l.push_back(h); }
// ok as handle can be cast to const_handle
); // for_each
}
private:
objList _obl;
};
So this solution actually works as a const myCollection would only allow you to get a list of const_handle which only allows you to call non-modifying methods of myObj (GOOD).
The problem is that the "read" method is really ugly (BAD).
Another method would be to expose somehow the list methods and return const_handle and handle as needed but it's a lot of overhead, especially if you want to use something more complex than a list.
Any idea?
A list-of-pointers-to-T is not a list-of-pointers-to-constant-T.
std::list<std::shared_ptr<int>> a;
std::list<std::shared_ptr<const int>>& ra = a; // illegal but imagine it's not
std::shared_ptr<const int> x = std::make_shared<const int>(42);
ra.push_back(x); // totally legal, right?
++**a.begin(); // oops... just incremented a const int
Now a list-of-pointers-to-T is, conceptually, a constant-list-of-constant-pointers-to-constant-T, but std::list<std::shared_ptr<T>> does not support such a deep const propagation. const std::list<std::shared_ptr<T>> contains constant pointers to non-constant objects.
You can write your own variant of list<> or your own variant of shared_ptr<> that have such support. It probably won't be very easy though. A const_propagating_shared_ptr is probably the easier of the two. It would have to encapsulate an std::shared_ptr<T> object and forward almost everything to it as-is. As opposed to std::shared_ptr<T> it would have separate const and non-const versions of operator->, operator*() and get().
Given what you stated that you want to accomplish, I don't think that your solution is too bad. Imagine that some other code may be modifying the internal collection, like adding or removing values. Returning a copy of the current state of the collection is safe for client code, since it can work on the copy, without the danger of element being deleted in the meantime. But I digress, this is getting into threading issues and may not be relevant.
You could use prettier:
inline const_objList read() const
{
const_objList cl(_obl.begin(), _obl.end());
return cl;
}
However, I do think that your problems derive from mixing two types of constness: constness of the members of the collection versus the constness of the collection itself.
Instead of Modify and Read methods, that deal with the list as a whole, I would try exposing const and non-const iterators to internal list, through corresponding const and non-const methods returning said iterators.
But this immediately begs the question: why then have myCollection in the first place?
Creating entirely new collection type around std::list doesn't seem needed, unless you get a lot of proverbial bang for the buck from other, added functionality that is not visible in your sample.
You can then make your added functionality free methods that take std::list of your handles as the input. Not everything requires an object and operations on objects need not necessarily be member methods, unless access to private data is required.
You mentioned maybe using another container instead of the list. But your class, as is, won't do it, unless you have a template, where template parameter can be one of STL containers.
Which then implies that you should expose iterators.
Namely, if you foresee changing the internal collection type, you would want to make the public interface to myCollection transparent regarding the collection type. Otherwise, clients will have to recompile each time you change your mind about the internal implementation.
EDIT -----
Finally, if implementing iterators (while interesting and most correct) is too much, why not go for simple getters like in this SO post:
smart pointer const correctness
I'll quote the topmost answer by RĂ¼diger Stevens (it assumes vector instead of list):
template <typename T>
class MyExample
{
private:
vector<shared_ptr<T> > data;
public:
shared_ptr<const T> get(int idx) const
{
return data[idx];
}
shared_ptr<T> get(int idx)
{
return data[idx];
}
void add(shared_ptr<T> value)
{
data.push_back(value);
}
};
Related
I am starting using c++11 features and I like to use smart pointers only to own the objects. Here is my class:
class MyClass {
public:
vector<MyObject*> get_objs() const;
private:
vector<unique_ptr<MyObject>> m_objs;
};
The semantics is that MyClass owns a serial of MyObject which are created through make_unique(). get_objs() returns a vector of raw pointers in order for various callers to update the objects. Because those callers do not own the objects, so the function does not return vector<unique_ptr>.
But this means I need to implement get_objs() like this:
vector<MyObjects*> MyClass::get_objs() const
{
vector<MyObjects*> ret;
for (auto obj : my_objs) {
ret.push_back(obj->get());
}
return ret;
}
My concern is get_objs() is called fairly often, each time there is an overhead to construct this raw pointer vector.
Is there something I could do here? If there is no c++11 tricks to save the overhead, should I just use type vector<MyObject*> for m_objs in the first place?
UPDATE 1
Jonathan Wakely's solution using operator[] improves mine so that caller can access individual object directly.
Is there any other solution? I do not mind go over all the places calling get_objs(), but like to see if there is even better solution.
Another note - I cannot use BOOST, just some restriction I have to live with.
For a start you can use ret.reserve(m_objs.size()) to pre-allocate the right number of elements.
Alternatively, don't return a vector for callers to iterate over directly, but expose a vector-like interface instead:
class MyClass {
public:
struct iterator;
iterator begin();
iterator end();
MyObject* operator[](size_t n) { return m_objs[n].get(); }
private:
vector<unique_ptr<MyObject>> m_objs;
};
This allows the callers to modify the objects directly, rather than getting a container of pointers.
class MyClass {
public:
std::vector<std::unique_ptr<MyObject>> const& get_objs() const {
return m_objs;
}
private:
std::vector<std::unique_ptr<MyObject>> m_objs;
};
a const std::unique_ptr<MyObject>& cannot steal ownership, and is not the same as a std::unique_ptr<const MyObject>. A const std::vector<std::unique_ptr<MyObject>>& can only grant const access to its data.
In c++20 I would instead do this:
class MyClass {
public:
std::span<std::unique_ptr<MyObject> const> get_objs() const {
return {m_objs.begin(), m_objs.end()};
}
private:
std::vector<std::unique_ptr<MyObject>> m_objs;
};
which hides the implementation detail of "I am storing it in a vector" while exposing "I am storing it contiguously".
Prior to c++20, I advise finding or writing your own span type if you have the budget. They are quite useful.
If you can use Boost, try indirect_iterator (http://www.boost.org/doc/libs/1_55_0b1/libs/iterator/doc/indirect_iterator.html). You need to define iterator, begin and end in your class:
typedef boost::indirect_iterator<vector<unique_ptr<MyObject>::iterator> iterator;
iterator begin() { return make_indirect_iterator(m_objs.begin()); }
Then your class exposes iterator, the value of which is reference (not pointer!) to MyObject. You can iterate and access the elements of the vector directly.
For the record, I think something like Jonathan Wakely's answer is the way to go. But since you asked for more possibilities, another one is to use shared_ptr instead of unique_ptr:
class MyClass {
public:
const vector<shared_ptr<MyObject>>& get_objs() const {
return m_objs;
}
private:
vector<shared_ptr<MyObject>> m_objs;
};
This improves the original code in two ways:
There is no longer any need to build up a new vector in get_objs; you can just return a reference to the one you have.
You no longer need to worry about wild pointers in the case where a caller keeps the return value alive longer than the object that returned it--shared_ptr ensures the pointed-to objects aren't deleted until all references have been released.
On another note, get_objs arguably should not be const. Calling code can't modify the vector itself, but it can modify the MyObjects it contains.
Another way is not to return any objects at all and encapsulate how you are storing the data (tell don't ask).
Usually when you get the items you end up iterating through them all, so it makes sense to wrap that up in the class and pass in a function you want your objects to pass through.
The non boost way will be something like
void MyClass::VisitItems(std::function<void, MyObject&> f)
{
for (auto obj : my_objs)
{
f(*obj);
}
}
The following is said to be better than having First() and Second() as public members. I believe this is nearly as bad.
// Example 17-3(b): Proper encapsulation, initially with inline accessors. Later
// in life, these might grow into nontrivial functions if needed; if not, then not.
template<class T, class U>
class Couple {
public:
Couple() : deleted_(false) { }
void MarkDeleted() { deleted_ = true; }
bool IsDeleted() { return deleted_; }
private:
T first_;
U second_;
bool deleted_;
T& First() { return first_; }
U& Second() { return second_; }
};
If you're giving a way to access a private variable outside of the class then what's the point? Shouldn't the functions be
T First(); void(or T) First(const T&)
There are several reasons why returning references (or pointers) to the internals of a class are bad. Starting with (what I consider to be) the most important:
Encapsulation is breached: you leak an implementation detail, which means that you can no longer alter your class internals as you wish. If you decided not to store first_ for example, but to compute it on the fly, how would you return a reference to it ? You cannot, thus you're stuck.
Invariant are no longer sustainable (in case of non-const reference): anybody may access and modify the attribute referred to at will, thus you cannot "monitor" its changes. It means that you cannot maintain an invariant of which this attribute is part. Essentially, your class is turning into a blob.
Lifetime issues spring up: it's easy to keep a reference or pointer to the attribute after the original object they belong to ceased to exist. This is of course undefined behavior. Most compilers will attempt to warn about keeping references to objects on the stack, for example, but I know of no compiler that managed to produce such warnings for references returned by functions or methods: you're on your own.
As such, it is usually better not to give away references or pointers to attributes. Not even const ones!
For small values, it is generally sufficient to pass them by copy (both in and out), especially now with move semantics (on the way in).
For larger values, it really depends on the situation, sometimes a Proxy might alleviate your troubles.
Finally, note that for some classes, having public members is not so bad. What would be the point of encapsulating the members of a pair ? When you find yourself writing a class that is no more than a collection of attributes (no invariant whatsoever), then instead of getting all OO on us and writing a getter/setter pair for each of them, consider making them public instead.
If template types T and U are big structures then return by value is costly. However you are correct that returning by reference is equivalent to giving access to a private variable. To solve both issues, make them const references:
const T& First() const { return first_; }
const U& Second() const { return second_; }
P.S. Also, it's a bad practice to keep variables uninitialized inside constructor, when there is no setter method. It seems that in the original code, First() and Second() are wrappers over first_ and second_ which were meant for read/write both.
The answer depends on what one is trying to do. Returning references are a convenient way to facilitate mutation of data structures. A good example is the stl map. It returns reference to the element i.e.
std::map<int,std::string> a;
a[1] = 1;
nothing to stop you from doing
auto & aref = a[1];
Is it necessarily a bad practice? I would not think so. I would say, if you can do without it do so. If it makes life more convenient and efficient use it and be aware of what you are doing.
The story begins with something I thought pretty simple :
I need to design a class that will use some STL containers. I need to give users of the class access to an immutable version of those containers. I do not want users to be able to change the container (they can not push_back() on a list for instance), but I want users to be able to change the contained objects (get an element with back() and modify it) :
class Foo
{
public:
// [...]
ImmutableListWithMutableElementsType getImmutableListWithMutableElements();
// [...]
};
// [...]
myList = foo.getImmutableListWithMutableElements();
myElement = myList.back();
myElement.change(42); // OK
// [...]
// myList.push_back(myOtherElement); // Not possible
At first glance, it seems that a const container will do. But of course, you can only use a const iterator on a const container and you can not change the content.
At second glance, things like specialized container or iterator come to mind. I will probably end up with that.
Then, my thought is "Someone must have done that already !" or "An elegant, generic solution must exist !" and I'm here asking my first question on SO :
How do you design / transform a standard container into an immutable container with mutable content ?
I'm working on it but I feel like someone will just say "Hey, I do that every time, it's easy, look !", so I ask...
Thank you for any hints, suggestions or wonderful generic ways to do that :)
EDIT:
After some experiments, I ended up with standard containers that handle some specifically decorated smart pointers. It is close to Nikolai answer.
The idea of an immutable container of mutable elements is not a killing concept, see the interesting notes in Oli answer.
The idea of a specific iterator is right of course, but it seems not practical as I need to adapt to any sort of container.
Thanks to you all for your help.
The simplest option would probably be a standard STL container of pointers, since const-ness is not propagated to the actual objects. One problem with this is that STL does not clean up any heap memory that you allocated. For that take a look at Boost Pointer Container Library or smart pointers.
Rather than providing the user with the entire container, could you just provide them non-const iterators to beginning and end? That's the STL way.
You need a custom data structure iterator, a wrapper around your private list.
template<typename T>
class inmutable_list_it {
public:
inmutable_list_it(std::list<T>* real_list) : real_list_(real_list) {}
T first() { return *(real_list_->begin()); }
// Reset Iteration
void reset() { it_ = real_list_->begin(); }
// Returns current item
T current() { return *it_; }
// Returns true if the iterator has a next element.
bool hasNext();
private:
std::list<T>* real_list_;
std::list<T>::iterator it_;
};
The painful solution:
/* YOU HAVE NOT SEEN THIS */
struct mutable_int {
mutable_int(int v = 0) : v(v) { }
operator int(void) const { return v; }
mutable_int const &operator=(int nv) const { v = nv; return *this; }
mutable int v;
};
Excuse me while I have to punish myself to atone for my sins.
I need to store a list of various properties of an object. Property consists of a name and data, which can be of any datatype.
I know I can make a class "Property", and extend it with different PropertySubClasses which only differ with the datatype they are storing, but it does not feel right.
class Property
{
Property(std::string name);
virtual ~Property();
std::string m_name;
};
class PropertyBoolean : Property
{
PropertyBoolean(std::string name, bool data);
bool m_data;
};
class PropertyFloat : Property
{
PropertyFloat(std::string name, float data);
float m_data;
};
class PropertyVector : Property
{
PropertyVector(std::string name, std::vector<float> data);
std::vector<float> m_data;
};
Now I can store all kinds of properties in a
std::vector<Property*>
and to get the data, I can cast the object to the subclass. Or I can make a pure virtual function to do something with the data inside the function without the need of casting.
Anyways, this does not feel right to create these different kind of subclasses which only differ by the data type they are storing. Is there any other convenient way to achieve similar behavior?
I do not have access to Boost.
C++ is a multi-paradigm language. It shines brightest and is most powerful where paradigms are mixed.
class Property
{
public:
Property(const std::string& name) //note: we don't lightly copy strings in C++
: m_name(name) {}
virtual ~Property() {}
private:
std::string m_name;
};
template< typename T >
class TypedProperty : public Property
{
public:
TypedProperty (const std::string& name, const T& data)
: Property(name), m_data(data);
private:
T m_data;
};
typedef std::vector< std::shared_ptr<Property> > property_list_type;
Edit: Why using std::shared_ptr<Property> instead of Property*?
Consider this code:
void f()
{
std::vector<Property*> my_property_list;
for(unsigned int u=0; u<10; ++u)
my_property_list.push_back(new Property(u));
use_property_list(my_property_list);
for(std::vector<Property*>::iterator it=my_property_list.begin();
it!=my_property_list.end(); ++it)
delete *it;
}
That for loop there attempts to cleanup, deleting all the properties in the vector, just before it goes out of scope and takes all the pointers with it.
Now, while this might seem fine for a novice, if you're an only mildly experienced C++ developer, that code should raise alarm bells as soon as you look at it.
The problem is that the call to use_property_list() might throw an exception. If so, the function f() will be left right away. In order to properly cleanup, the destructors for all automatic objects created in f() will be called. That is, my_property_list will be properly destroyed. std::vector's destructor will then nicely cleanup the data it holds. However, it holds pointers, and how should std::vector know whether these pointers are the last ones referencing their objects?
Since it doesn't know, it won't delete the objects, it will only destroy the pointers when it destroys its content, leaving you with objects on the heap that you don't have any pointers to anymore. This is what's called a "leak".
In order to avoid that, you would need to catch all exceptions, clean up the properties, and the rethrow the exception. But then, ten years from now, someone has to add a new feature to the 10MLoC application this has grown to, and, being in a hurry, adds code which leaves that function prematurely when some condition holds. The code is tested and it works and doesn't crash - only the server it's part of now leaks a few bytes an hour, making it crash due to being out of memory about once a week. Finding that makes for many hours of fine debugging.
Bottom line: Never manage resources manually, always wrap them in objects of a class designed to handle exactly one instance of such a resource. For dynamically allocated objects, those handles are called "smart pointer", and the most used one is shared_ptr.
A lower-level way is to use a union
class Property
union {
int int_data;
bool bool_data;
std::cstring* string_data;
};
enum { INT_PROP, BOOL_PROP, STRING_PROP } data_type;
// ... more smarts ...
};
Dunno why your other solution doesn't feel right, so I don't know if this way would feel better to you.
EDIT: Some more code to give an example of usage.
Property car = collection_of_properties.head();
if (car.data_type == Property::INT_PROP) {
printf("The integer property is %d\n", car.int_data);
} // etc.
I'd probably put that sort of logic into a method of the class where possible. You'd also have members such as this constructor to keep the data and type field in sync:
Property::Property(bool value) {
bool_data = value;
data_type = BOOL_PROP;
}
I suggest boost::variant or boost::any. [Related question]
Write a template class Property<T> that derives from Property with a data member of type T
Another possible solution is to write a intermediate class managing the pointers to Property classes:
class Bla {
private:
Property* mp
public:
explicit Bla(Property* p) : mp(p) { }
~Bla() { delete p; }
// The standard copy constructor
// and assignment operator
// aren't sufficient in this case:
// They would only copy the
// pointer mp (shallow copy)
Bla(const Bla* b) : mp(b.mp->clone()) { }
Bla& operator = (Bla b) { // copy'n'swap trick
swap(b);
return *this;
}
void swap(Bla& b) {
using std::swap; // #include <algorithm>
swap(mp, b.mp);
}
Property* operator -> () const {
return mp;
}
Property& operator * () const {
return *mp;
}
};
You have to add a virtual clone method to your classes returning a pointer to a newly created copy of itself:
class StringProperty : public Property {
// ...
public:
// ...
virtual Property* clone() { return new StringProperty(*this); }
// ...
};
Then you'll be able to do this:
std::vector<Bla> v;
v.push_back(Bla(new StringProperty("Name", "Jon Doe")));
// ...
std::vector<Bla>::const_iterator i = v.begin();
(*i)->some_virtual_method();
Leaving the scope of v means that all Blas will be destroyed freeing automatically the pointers they're holding. Due to its overloaded dereferencing and indirection operator the class Bla behaves like an ordinary pointer. In the last line *i returns a reference to a Bla object and using -> means the same as if it was a pointer to a Property object.
A possible drawback of this approach is that you always get a heap operation (a new and a delete) if the intermediate objects must be copied around. This happens for example if you exceed the vector's capacity and all intermediate objects must be copied to a new piece of memory.
In the new standard (i.e. c++0x) you'll be able to use the unique_ptr template: It
can be used inside the standard containers (in contrast to the auto_ptr which must not be used in the standard containers),
offers the usually faster move semantics (it can easily passed around) and
takes care over the held pointers (it frees them automatically).
I see that there are lots of shots at trying to solve your problem by now, but I have a feeling that you're looking in the wrong end - why do you actually want to do this in the first place? Is there some interesting functionality in the base class that you have omitted to specify?
The fact that you'd be forced to switch on a property type id to do what you want with a specific instance is a code smell, especially when the subclasses have absolutely nothing in common via the base class other than a name (which is the type id in this case).
Starting with C++ 17 we have something called as std::variant and std::any.
std::variant
An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value.
std::any
The class any describes a type-safe container for single values of any copy constructible type.
An object of class any stores an instance of any type that satisfies the constructor requirements or is empty, and this is referred to as the state of the class any object. The stored instance is called the contained object. Two states are equivalent if they are either both empty or if both are not empty and if the contained objects are equivalent.
The non-member any_cast functions provide type-safe access to the contained object.
You can probably do this with the Boost library, or you could create a class with a type code and a void pointer to the data, but it would mean giving up some of the type safety of C++. In other words, if you have a property "foo", whose value is an integer, and give it a string value instead, the compiler will not find the error for you.
I would recommend revisiting your design, and re-evaluating whether or not you really need so much flexibility. Do you really need to be able to handle properties of any type? If you can narrow it down to just a few types, you may be able to come up with a solution using inheritance or templates, without having to "fight the language".
I wrote a little "lazy vector" class (or, delayed vector) which is supposed to look like a std::vector and usable wherever a std::vector is used, but it loads its elements "lazily", i.e. it will load element n (and possibly a few more) from disk whenever someone accesses element n. (The reason is that in my app, not all elements fit into memory.)
Here is this LazyVector class, but there is a problem with const member functions that use such a vector, see below.
template<class T>
class LazyVector {
std::vector<T> elems_;
void fetchElem(unsigned n){
// load the n-th elem from disk into elems_ etc
}
public:
const T& operator[](unsigned n) const {
fetchElem(n); // ERROR: ... discards qualifiers
return elems_[n];
}
T& operator[](unsigned n) {
fetchElem(n);
return elems_[n];
}
// and provide some other std::vector functions
};
As I said, there is a problem when a const member function asks for an element of the LazyVector. By nature of the LazyVector, accessing an element is not const, i.e. it will change the vector vec below, which is forbidden in this context. The foo member function must be const and cannot be changed. How can I solve this?
class Foo {
LazyVector<const std::string*> vec;
void fct(int n) const { // fct must be const
const std::string* str = vec[n];
// do something with str
}
};
You can either use mutable member data or const_cast in the implementation of your LazyVector class. Thus you can create the illusion of constness needed by your consuming class without actually being const.
Use the mutable keyword on the elems_ data member.
The const operator is used to show that the object is logically const.
The fact that your data is on disk is neither here nor there your object is not changing state so you can delegate the work for actually holding the data to another object a cache (Where the data is stored is an implementation details and not part of the objects state).
class LazyVector
{
public:
int const& operator[](int index) const
{
data->fetchElement(index);
return data->get(index);
}
private:
std::auto_ptr<LazyDataCache> data;
};
Here data is a pointer (a smart pointer but still a pointer). As long as the pointer does not change you are not changing the cost-ness of the LazyVector. But you can still call non const methods on the object pointed at by data (remember it is the pointer that is const NOT the object pointed at).
For such things, the mutable keyword is for. Put your cache as a mutable object into your class. That is because your cache seems to not change the logical content/state of your object (i.e the elements of your vector or the size of it do not change).
const methods do not state they don't physically change your object. They state that they won't change the abstract value of your object. Implementation details that are abstracted away may still be changed by const functions.
The mutable is for this kind of cases. Make your vector mutable or add a mutable cache member that contains some sort of cache entries.
Read the What are the semantics of a const member function answer by Anthony Williams.
Declare elems_ as mutable:
mutable std::vector<T> elems_;
There's other stuff you can do, but that's the supported way of doing it.
Edit: Another way of doing this is to add another member and set it in the constructor:
std::vector<T> *mutable_elems_;
mutable_elems_(&elems_)
A crude way of doing this would be
LazyVector* p = const_cast<LazyVector*>(this);
p->fetch();
I guess there will be better way of doing this. But this will work.