Immutable object in collections (C++ and Qt) - c++

I am stuck with using immutable objects with collections. Let assume I have the following class :
//in .h
class Data {
public:
Data(const DataObj& data);
Data(const Data&);
const DataObj& getDataObj() const;
private:
const DataObj _data; //actually several objects or simple type
}
inline const DataObj& Data::getDataObj() const {return _data};
//in .c
Data(const DataObj& data) : _data(data){};
Data(const Data& original) : _data(original._data){}
The issue is that when I want to use collections, I have the error
in member function Data&Data::operator(const Data&);
instantiation from 'typename QMap<Key,T>::iterator QMap<Key, T>::insert(const Key&, const T&)[with Key = int, T = Data]'
instantiation from here
error : non-static const member 'const DataObj Data::_data', can't use default assignment operator
Now defining an assignment operator doesn't seems to make sense, since its prototype will be
Data& operator=(const Data&);
What should I do? Am I forced to remove all constant qualifiers in order to use my class inside collections? Or use pointers?

If you are instantiating your map like this
QMap <MyKey, Data>
I guess you should always define an assignment operator for Data (default or your own).
You should try using pointers, as you suggest, like this
QMap <Mykey, Data*>
QMap <Mykey, QSharedPointer<Data>>
If you take a look to the QMap code http://code.woboq.org/kde/include/QtCore/qmap.h.html, some operators/members return < T >, so it would need assignment defined.

Use good stl containers instead of bad Qt:
This will fail to work with Qt container due to COW, but it ok (until you try to copy the container) with stl
class C
{
C operator=(const C&);
};
int main()
{
std::map<int, C> cc;
cc.insert(std::make_pair(0, C()));
}

You can make the data member non-const, but provide only const access to users of the class, except for the assignment operator:
class Data {
public:
Data(const DataObj& data);
Data(const Data&);
Data& operator=(const Data&);
const DataObj& getDataObj() const;
private:
DataObj _data;
};
In this way, assignment is the only operation that can mutate an existing object. You would then have to make sure you provide access to const instances of this type in any public interfaces.

Related

is default enough for a c'tor d'tor of a class with vector elements only?

I have a class called Item that has two vectors as private elements
class Item
{
private:
std::vector<std::string> V;
std::vector<std::string> E;
public:
Item(std::vector<std::string> V,std::vector<std::string> E): V(V),E(E){}
Item(const Item& Item)=default;
~Item()=default;
Item& operator=(const Item& Item)=default;
};
as you can see I've used the default for the d'tor, copy c'tor and operator =,
but is that enough or should I write bodies for each of them?
You don't need to write your own, but I'd modify the declaration of your constructor.
Item(const std::vector<std::string>& v, const std::vector<std::string>& e):V(v), E(e){}
Always prefer passing large object by const reference to by value. Passing object by value may causes unnecessary copy of object.

How to prevent value assignment with inherited operator[]?

I'm having a custom structure called SortedArrayList<T> which sorts its elements according to a comparator, and I would like to prevent assigning using operator[].
Example:
ArrayList.h
template <typename T> class ArrayList : public List<T> {
virtual T& operator[](const int& index) override; //override List<T>
virtual const T operator[](const int& index) const override; //override List<T>
}
SortedLinkedList.h with following operators
template <typename T> class SortedArrayList : public ArrayList<T> {
public:
SortedArrayList<T>(const std::function<bool(const T&, const T&)>& comparator);
T& operator[](const int& index) override; //get reference (LHS)
const T operator[](const int& index) const override; //get copy (RHS)
}
Test.h
ArrayList<int>* regular = new ArrayList<int>();
ArrayList<int>* sorted = new SortedArrayList<int>(cmpfn);
(*regular)[0] == 5; //allow
(*regular)[0] = 5; //allow
(*sorted)[0] == 7; //allow
(*sorted)[0] = 7; //except
Is this operation possible?
By prevent I mean throwing an exception or something what will warn user to not do it.
Prefer aggregation over inheritance:
template <typename T> class SortedArrayList {
ArrayList<T> m_the_list;
public:
SortedArrayList<T>(const std::function<bool(const T&, const T&)>& comparator);
const T& operator[](const int& index) const {return m_the_list[index];}; // always get const reference
// Can act as a *const* ArrayList<T>, but not as a mutable ArrayList<T>, as that would violate Liskov's substitution principle.
operator const ArrayList<T>&() const {return m_the_list;}
}
As Stephen Newell correctly points out, when you're using inheritance, you're guaranteeing your class SortedArrayList can act as an ArrayList in every possible scenario. This is clearly not the case in your example.
You can read more here about how violating Liskov's Substitution Principle is a bad idea.
You should not do this. It indicates an improper design See the C++ FAQ on Inheritance. Your subclass doesn't fulfill the "is-a" requirement for public inheritance if it can't be used in all ways as the base class (LSP).
If you want to have one type of container that allows member replacement and another that doesn't, then the define the base class that just allows const member access (no need to make it virtual). Then branch from there to MutableList and ImmutableList, and let SortedArrayList derive from Immutable list.
Seems to me like the best practice here would be to implement an at(const int& index) method instead of overloading []. That would be more clear to the user of the interface anyway.
There is a similar function in std::map and other std data structures. For example: http://www.cplusplus.com/reference/map/map/at/
Why do you pass the index as reference at all? Absolutely no need for...
I personally recommend to use unsigned integer types for array indices (what would be the meaning of a negative index anyway???).
const for a type returned by value is (nearly) meaningless - it will be copied to another variable anyway (which then will be modifiable), but you prevent move semantics...
So:
T& operator[](unsigned int index); //get reference (LHS)
T operator[](unsigned int index) const; //get copy (RHS)
(Just some improvement suggestions...)
Now to the actual question: Disallowing modification is quite easy:
//T& operator[](unsigned int index); //get reference (LHS)
T const& operator[](unsigned int index) const; //get copy (RHS)
Just one single index operator, always returning const reference... If user can live with reference, fine, otherwise he/she will copy the value anyway...
Edit in adaption to modified question:
As now inheritance is involved, the stuff gets more complicated. You cannot just get rid of some inherited function, and the inherited one will allow element modification.
In the given situation, I'd consider a redesign (if possible):
class ArrayListBase
{
public:
T const& operator[](unsigned int index) const;
// either copy or const reference, whichever appears more appropriate to you...
};
class ArrayList : public ArrayListBase
{
public:
using ArrayListBase::operator[];
T& operator[](unsigned int index);
}
class SortedArrayList : public ArrayListBase
{
public:
// well, simply does not add an overload...
}
The insertion function(s) might be pure virtual in the base class (where a a common interface appears suitable) or only available in the derived classes. Decide you...

Overloading specialized assignment operator based on typename

Okay, the title is a mouthful and hopefully specific enough, but I'm running into a C++ issue which I think is possible, I just can't seem to find the proper syntax.
I have a simple template-based property class:
template <typename T>
class Property
{
public:
Property<T> &operator = (const T &src)
{
m_data = src;
return *this;
};
operator const T& () const
{
return m_data;
}
private:
T m_data;
};
I need to assign values from a QVariant, which is also sort of a property but without templates. At the moment I am deferencering the values explicitly when assigning:
Property<QString> p1;
Property<int> p2;
p1 = var1.toString();
p2 = var2.toInt();
This works but it is tedious and I'm sure the compiler can do the work for me. So, I tried implementing specialized assignment operators based on the typename of the Property; in other words, if the template specialization is based on a QString use one function, if it is based on an int use another function, et cetera.
I tried things like:
Property<QString> &operator = (const QVariant &ref)
{
m_data = ref.toString ();
return *this;
};
or...
template <typename int> &Property<int>::operator = (const QVariant &ref)
{
m_data = ref.toInt ();
return *this;
};
or...
template<> Property<T> &Property<QString>::operator = (const QVariant &ref)
{
m_data = ref.toString ();
return *this;
}
..both inside and outside of the class declaration but to no avail. I get errors like "expected nested-name-specifiers", "two or more data types in declaration of parameters" and the like.
Any pointers to the correct syntax would be appreciated!
PS: the first declaration compiles witout error, but adding the same declaration for <int> makes it ambiguous since only the return type differs.
Compiler is GCC 4.8.2 with --stdc=c++11 enabled.
Although the answer of paddy seems correct, it seems to me tedious to generalize it (implement a specialzed assignment operator to all the types that a QVariant can hold, in addition to QString and int).
An easier solution would be to specialize the assignment operator once for all, in a way that benefits from the built-in conversions that the QVariant provides:
Property& operator= (const QVariant& src)
{
assert(src.canConvert<T>());
m_data = src.value<T>();
return *this;
}
The original class definition is a bit wrong, as pointed out by Kirill Kobelev in your question's comments section. Now, let's fix that and also add another operator=:
template <typename T>
class Property
{
public:
Property & operator=( const T & src )
{
m_data = src;
return *this;
}
// This one must be specialized
Property & operator=( const QVariant & src );
const T & operator() const
{
return m_data;
}
private:
T m_data;
};
The specialization is therefore quite simple. Here's ones for QString and int:
template <>
Property<QString> &
Property<QString>::operator=( const QVariant & src )
{
return operator=( src.toString() );
}
template <>
Property<int> &
Property<int>::operator=( const QVariant & src )
{
return operator=( src.toInt() );
}
Now you are free to assign QVariant values directly to any Property which provides that specialization. If you try to do it on a class that did not specialize then you will get an error.
I don't have the reputation to comment, nor the expertise to claim insight so please don't take this as an "answer".
But your question reminded me of a lesson on Cpp, Overloads and Functions over at http://www.cplusplus.com/doc/tutorial/functions2/ .
Excerpt:
"In the example above, we used the function template sum twice. The first time with arguments of type int, and the second one with arguments of type double. The compiler has instantiated and then called each time the appropriate version of the function.
Therefore, result will be a variable of the same type as the parameters a and b, and as the type returned by the function.
In this specific case where the generic type T is used as a parameter for sum, the compiler is even able to deduce the data type automatically without having to explicitly specify it within angle brackets."
Apologies if I missed the point, but thought this may help.

Using boost::visitor with a boost::variant of unique_ptr

I have two types of std::unique_ptr which are held inside a boost::variant. I'm trying to write a subclass of boost::static_visitor to extract a const reference to to the underlying object the two unique_ptr variants my boost::variant is templated on. The setup looks something like this:
using bitmap_flyweight_t = boost::flyweights::flyweight<allegro_bitmap_t>;
using image_bitmap_flyweight_t = boost::flyweights::flyweight<boost::flyweights::key_value<const char*, allegro_bitmap_t>>;
class bitmap_visitor : public boost::static_visitor<allegro_bitmap_t>
{
public:
const allegro_bitmap_t& operator()(const std::unique_ptr<bitmap_flyweight_t> bitmap_ptr) const
{
return bitmap_ptr.get()->get();
}
const allegro_bitmap_t& operator()(const std::unique_ptr<image_bitmap_flyweight_t> bitmap_ptr) const
{
return bitmap_ptr.get()->get();
}
};
I'm able to put the unique_ptrs into an object's boost::variant member variable on instantiation using move semantics, no compiler complaints there. However, when I try to access the variant type using the above visitor, the compiler complains that it can't do it, as unique_ptr is not copy constructible.
Accept a const reference to the unique pointer.
class bitmap_visitor : public boost::static_visitor<allegro_bitmap_t>
{
public:
const allegro_bitmap_t& operator()(const std::unique_ptr<bitmap_flyweight_t>& bitmap_ptr) const
{
return bitmap_ptr.get()->get();
}
const allegro_bitmap_t& operator()(const std::unique_ptr<image_bitmap_flyweight_t>& bitmap_ptr) const
{
return bitmap_ptr.get()->get();
}
};
Here's a live demo of a toy project.

const members and operator=

I have a struct with some const variables
struct HashData
{
const HashKey key;
const void* data;
HashData(const HashKey& key_, const void* data_) : key(key_), data(data_) {}
/* How to write this ?
HashData operator=(const HashData & data)
{
key = std::move(data.key);
return *this;
}
*/
};
and another class where i use it.
class HashTable
{
std::vector< std::list<HashData> > hashTable; ///< Collisions resolution by chaining.
public:
void insertAtIndex(const std::size_t index, const HashData& data) {
hashTable[index].insert(std::begin(hashTable[index]), data);
}
};
class HashTable compiles but another class
class OpenAddressHashTable
{
std::vector<HashData> hashTable;
public:
void insert(const HashData & data) throw() {
if (data.key == NULLKEY)
throw std::runtime_error("Do not use NullKey");
size_t iteration = 0;
do {
const size_t index = (*hashFunc)(data.key, iteration);
if (hashTable[index].key == NULLKEY) {
// space is free
// ** IMPORTANT **///// Line 131 is next line
hashTable[index] = data;
return ;
}
iteration++;
} while(iteration < hashTable.size());
throw std::runtime_error("No space left");
}
};
I get this error :
g++ -W -Wall -pedantic -std=c++11 hash.cpp
hash.cpp: In member function 'void OpenAddressHashTable::insert(const HashData&)':
hash.cpp:131:24: error: use of deleted function 'HashData& HashData::operator=(const HashData&)'
hash.cpp:26:8: note: 'HashData& HashData::operator=(const HashData&)' is implicitly deleted because the default definition would be ill-formed:
hash.cpp:26:8: error: non-static const member 'const HashKey HashData::key', can't use default assignment operator
What is it that std::list does that i need to put the data in my vector ?
Do i need to use pointers in my hashTable ?
You are doing an assignment:
hashTable[index] = data;
There is simply no way for that to work if you have const members because you cannot copy or move into const. The compiler error is pretty explicit:
[the assignment operator] is implicitly deleted because the default definition would be ill-formed
What would you expect the assignment to do to key and data? The simplest thing would be to drop the const and enforce it on your interface with the user - so that they cannot change the key out from under you. For instance:
using InternalHashData = std::pair<HashKey, void*>;
using ExternalHashData = std::pair<const HashKey, void*>;
InternalHashData& something_to_be_returned = ..; // never expose this
return reinterpret_cast<ExternalHashData&>(something_to_be_returned); // this is OK
The only way I can think of to keep the const would be to change your table from:
std::vector<HashData> hashTable;
to
std::vector<std::unique_ptr<HashData>> hashTable;
But then you're doing an extra allocation on each insert just to preserve const-ness, which doesn't seem like a good tradeoff to me at all, especially for a container whose sole purpose is performance.
The compiler deletes the assignment operator for the class HashData because it has constant fields (key and data). This makes sense since you can't assign something to a const member, therefore the object were the const member is living is shouldn't be assigned ether.
Your HashData class is inmutable. This could be ok if you want the class to be so, but the assignment in your class OpenAddressHashTable can not be performed because of the inmutability.
Also regarding your "const void* data" field: Really more C++ like would be to use generics here.
You could do something like this:
template<typename T>
struct HashData
{
HashKey key; // delete the const if you really want to modify a instance of HashData
T data;
HashData(const HashKey& key_, T data_) : key(key_), data(data_) {}
};
And T will be the type of your mapped value. This of couse will force you that all your values have the same type, which might be not a problem.
hashTable[index].insert(std::begin(hashTable[index]), data);
Will insert a new HashData object at the front of the linked-list held at the index. This means a call to a copy constructor, which is defined by default for HashData. (A default copy constructor copy constructs all its members, and const members are copy construable since you are setting an initial value, not overwriting an existing one)
hashTable[index] = data;
Will assign to an existing HashData object, but assignment is deleted by default for HashData. (All its members are nonassignable because you've declared them all as const)
What you could do instead is have the members of HashData as non-const, but return only const references and const iterators to the private vector in OpenAddressHashTable. This will keep your HashData objects as const everywhere except where they are actually managed, which seems to be your goal.