Say I have a class that has only constants, since the values should never change.
struct Test {
const std::string id;
const int a;
const double b;
};
Later on, I want to add objects into a vector, but I want the vector to be sorted by b from largest to smallest. I use insertion sort, since there will only ever be a small number (maybe 5).
std::vector<Test> values;
void addValue( const std::string &id, int a, double b ) {
// Keep stockpiles sorted by weight (large to small)
auto itr = values.begin();
while ( itr != values.end() && itr->b > b ) ++itr;
values.insert( itr, { id, a, b } );
// end sort
}
Attempting the above, I get the following error while attempting to insert into the vector:
error: object of type 'Test' cannot be assigned because its copy assignment operator is implicitly deleted
I would like to keep with using a vector for this problem; but I can't seem to find a way around the sorting issue. The only other option I could think of was to effectively recreate the vector constantly. Alternatively, using a multi-set or similar, then once all values are added, I could dump into an array.
Is there a way around this limitation, still using a vector and not making everything non-const? Or will I be forced to change my structure, or move away into a temporary object first?
Edit: Also trying to avoid the use of pointers
As has already been pointed out in the comments, sorting requires moving around elements in your vector. And moving around elements generally requires move-assignment, which generally requires mutation of elements…
There is one way out of this dillema: instead of assigning a new value to the existing object, create a new object with the new value on top of the existing one. One can do so by defining a copy/move constructor like, e.g.:
Test& operator =(Test&& other)
{
this->~Test();
return *new (this) Test(std::move(other));
}
There is just one problem with this: via [basic.life]/8.3, we're not allowed to use any existing pointer or reference to the original object, or even just the name of the original object after such an assignment. You would always have to use the result of the assignment (the return value of placement-new) or a laundered pointer as the sole means of accessing the object going forward. Since it is not specified how std::sort operates exactly, we cannoy rely on it doing so.
What we could to is build a wrapper like this (noexcept ommited for readability):
template <typename T>
class const_element
{
T value;
const T& laundered_value() const { return *std::launder(&value); }
T& laundered_value() { return *std::launder(&value); }
public:
template <typename... Args>
explicit const_element(Args&&... args) : value { std::forward<Args>(args)... } {}
const_element(const const_element& e) : value(e.laundered_value()) {}
const_element(const_element&& e) : value(std::move(e.laundered_value())) {}
const_element& operator =(const const_element& e)
{
laundered_value().~T();
new (&value) T(e.laundered_value());
return *this;
}
const_element& operator =(const_element&& e)
{
laundered_value().~T();
new (&value) T(std::move(e.laundered_value()));
return *this;
}
~const_element()
{
laundered_value().~T();
}
operator const T&() const { return laundered_value(); }
operator T&() { return laundered_value(); }
friend bool operator <(const_element& a, const_element& b)
{
return a.laundered_value() < b.laundered_value();
}
};
What this does is wrap an object of some type T, implement copy and move assignment in the way described above, and make sure that any access to the current value always goes through a laundered pointer.
Then we can just do
std::vector<const_element<Test>> values;
values.emplace_back("b", 1, 0.0);
values.emplace_back("a", 0, 0.0);
values.emplace_back("c", 2, 0.0);
std::sort(begin(values), end(values));
working example here
All that being said, I would recommend to just not do this. If you want an object that cannot be modified, simply use a const T rather than a T that solely consists of const members. You cannot have a vector of const T. But you can have a vector of T and then just pass around a reference to a const vector or a range of const elements…
You could store pointers in the vector. Of course, you would also need to clean everything up. Example at http://cpp.sh/3h7dr
std::vector<Test*> values;
void addValue( const std::string &id, int a, double b ) {
// Keep stockpiles sorted by weight (large to small)
auto itr = values.begin();
while ( itr != values.end() && ((*itr)->b > b) ) ++itr;
Test* t = new Test{id,a,b};
values.insert( itr, t );
// end sort
}
Related
I have a slight problem. I have a Matrix class defined as follows (in row-major form):
template<typename T>
class Matrix {
private:
class RowVector {
private:
T *_vec;
std::size_t _l;
public:
RowVector(T *vec, std::size_t l);
const T &operator[](std::size_t index) const;
T &operator[](std::size_t index);
operator std::vector<T>() const;
};
std::vector<T> _data;
std::size_t _m;
std::size_t _n;
public:
Matrix(std::size_t m, size_t n, const T &elem = T());
const RowVector operator[](std::size_t index) const;
RowVector operator[](std::size_t index);
std::size_t getm() const;
std::size_t getn() const;
void fill(const T &elem);
void fillRow(std::size_t index, const T &elem);
void fillCol(std::size_t index, const T &elem);
Matrix &transpose(unsigned int i = 1);
const std::vector<T> &data() const;
};
and wish to overload two RowVector operators=
typename Matrix<T>::RowVector &operator=(const std::vector<T> &vec);
typename Matrix<T>::RowVector &operator=(const Matrix<T> &mat);
so I can return a RowVector & using A[0] and reassign its value using either a vector or a matrix. Keep in mind that I (presumably) can ignore the rule of three because I provide no explicit way for the client to construct a RowVector object.
However, in attempting to write the function bodies for the overloads, I have come across a problem: that
(1) I cannot copy-construct a vector/Matrix object that will persist outside of the operator='s scope so that I can assign its data() to _vec and its size() to _l.
(2) I cannot directly modify _data since it is not a static variable; even if I could, I have no way to discover the index so I can overwrite the relevant region of memory in the enclosing Matrix object.
Do you know of any way in which this can be done? These would be two very useful assets for my class.
I would like to be able to write something like this:
Matrix<int> A(3, 4);
std::vector<int> v {1, 2, 3, 4};
Matrix<int> row(1, 4, 3);
// *****************
A[0] = v;
A[1] = row;
// *****************
(Hopefully my variable names are self-explanatory)
I think my prototypes are correct, but I just can't find a way to do this.
Thanks!
T *_vec;
std::size_t _l;
This is a problematic design! I don't say it is incorrect per se, but you then need to correctly manage the memory yourself. Ignoring the rule of three (five) is a very dangerous in this respect. You have a pointer to (potentially?) dynamically allocated memory, so there must be some instance that is responsible for its deletion (must not necessarily be your RowVector, but what else then?).
From pure technical aspect, you even can let _vec point to some vector's data, provided you gurantee that this other vector lives as long as you want to access the data via pointer – which in general requires, though, quite some effort.
Safest would be having each row maintain its own data, copying (or moving) it from the other vector. Then easiest is storing the data in a std::vector of its own (replacing the raw pointer).
If you want to avoid copying data around and instead share data between different matrices and their rows, then you could maintain the data via a std::shared_ptr - either maintaining a raw array or possibly even a heap-allocated std::vector.
If you opt for either std::vector or std::shared_ptr, then copy and move constructors and assignment operators get totally simple:
class C
{
public:
C(C const&) = default;
C(C&&) = default;
C& operator= (C const&) = default;
C& operator= (C&&) = default;
};
All of these defaults will do copying/moving as per member, and both std::vector and std::shared_ptr have the appropriate constructors and operators available already, so you'd be fine – and you now can violate the rule of five, dropping the destructor, as the default one (calling all the member's destructors) suffices.
If you consider shared pointers: be aware that you then cannot assign a std::vector's data to: std::vector does its own memory management, and you will end up in double deletion of, so in this specific case, you'd still have to create a copy of. You might possibly end up in multiple constructors and assignment operators:
std::shared_ptr<std::vector<int>> _data;
// assign shared pointers
RowVector(RowVector const&) = default;
RowVector(RowVector&&) = default;
// need to create copies of: we never know about the scope of the vector passed!
RowVector(std::vector<int> const& data) : _data(new std::vector<int>(data)) { }
RowVector(std::vector<int>&& data) : _data(new std::vector<int>(std::move(data))) { }
// we *are* sharing already -> no need to copy:
RowVector(std::shared_ptr<std::vector<int>& data) : _data(data) { }
Assignment operators analogously.
Side-note: If you want to have a mathmatical nxm matrix, pretty sure you don't want to have a jagged array. I'd assume your Matrix class' constructor already creates an appropriate vector of vectors, then for assignment, you'd have yet to check length:
// defaults not suitable any more!
RowVector& RowVector::operator=(RowVector const& other)
{
// still assuming shared pointer:
// (for vector, replace -> with .)
if(other._data->size() != _data.size())
{
throw SomeException();
}
_data = other._data;
}
RowVector(RowVector&& other)
{
if(other._data->size() != _data.size())
{
throw SomeException();
}
_data = std::move(other._data);
}
I have a situation, where I would like to have a map that does not allow to add/remove keys after initialization, but the values are allowed to change (thus I cannot simply make the map const). Ie
/*semi-const*/ map<int,int> myMap = initMap();
myMap[1] = 2; // NOT OK, because potentially adds a new key
myMap.at(1) = 2; // OK, because works only if key is present
for (auto & element : myMap) {
element.second = 0; // OK, values may change
}
I could write my own wrapper for std::map, but I have the feeling that it is something not too uncommon, so I wonder if there is already an existing solution.
Is there some standard idiom for a map that does not allow adding/removing keys, while the values may change?
ps: I know that the title alone is a bit vague, because the keys are already const in a map, but I hope it is clear what I mean...
Could you create a wrapper that contains the value that allows the value to be mutated when const and put that in the map instead? Something like:
template<typename T>
class Mutable {
mutable T value;
public:
const Mutable& operator=(const T& v) const { value = v; return *this; }
T& get() const { return value; }
};
Then your map can be of type
const std::map<int, Mutable<int>>
Live demo.
I usually regard this as a pitfall in C++ more than a feature, but, if it fits your application, you can just use pointer values.
#include <map>
#include <memory>
int main(int argc, char ** argv)
{
using namespace std;
const map<int, shared_ptr<int>> myMap = { {1, make_shared<int>(100)} };
// *(myMap[1]) = 2; // Does not compile
*(myMap.at(1)) = 2;
for (auto & element : myMap)
{
*(element.second) = 0;
}
return 0;
}
Which is really just a simpler version of this other answer (obviously you may choose between shared_ptr / unique_ptr as needed).
Containers from the standard library are classes optimized for one usage that are expected to be used as is or included in higher level classes.
Here your requirement (keys fixed after initialization) is not covered by the standart library containers, so you will have to build your own implementation. As it will not be a std::map, you can just implement the operations you need, probably nothing more that operator []...
I understand that you simply want to disable the index access operator so that a user cannot accidentally add a default constructed item to the map. My solution is inspired by Chris Drew's solution but has the added benefit of remaining const correct (i.e. not allowing changing values of the map when the map is const).
Essentially, by disabling default construction you remove the ability to invoke the index access operator provided by std::map. The other methods will remain available since std::map is a class template and member functions won't be evaluated until they are invoked. Hence, std::map::at will work fine but std::map::operator[] will result in a compile-time error.
Inspired by Chris you can use a wrapper on the mapped_type to disable default construction. I took his demo and tweaked it a bit to demonstrate how to disable default construction and used it with std::map rather than a const std::map.
template<typename T>
class RemoveDefaultConstruction {
T value;
public:
RemoveDefaultConstruction() = delete; // The magic is here
RemoveDefaultConstruction(const RemoveDefaultConstruction &other) noexcept(std::is_nothrow_copy_constructible<T>::value) = default;
RemoveDefaultConstruction(RemoveDefaultConstruction &&other) noexcept(std::is_nothrow_move_constructible<T>::value) = default;
RemoveDefaultConstruction(T &&t) noexcept(std::is_nothrow_constructible<T, decltype(std::forward<T>(t))>::value) :
value{std::forward<T>(t)} {
}
RemoveDefaultConstruction& operator=(const RemoveDefaultConstruction &other) = default;
RemoveDefaultConstruction& operator=(RemoveDefaultConstruction &&other) = default;
RemoveDefaultConstruction& operator=(T &&other) { value = std::move(other); return *this; }
RemoveDefaultConstruction& operator=(T const &other) { value = other; return *this; }
T const &get() const { return value; } // Keep const correctness
T &get() { return value; } // Keep const correctness
};
void update(std::map<int, RemoveDefaultConstruction<int>> &m, int k, int v) { m.at(k) = v; }
void update(std::map<int, RemoveDefaultConstruction<int>> const &m, int k, int v) {
//m.at(k) = v; // ERROR: Cannot change a const value
}
Live Demo
I see 2 options here
Make the map const and use const_cast when changing something
const std::map myMap;
myMap[1] = 2; // NOT OK, because const map
(const_cast&>(myMap)).at(1) = 2; // OK with const_cast
make an wrapper class or derive an custom map that has only read and update existing value methods
I don't think there is an built in way to make an map only with update value, and restrict and insert.
How can I efficiently copy objects (or a range of objects) from vector A into vector B,
where vector B already contains certain objects identical to those from vector A,
so that no objects copied from vector A are already listed in vector B?
I have a graph stored as a vector of edges in std::vector<MinTreeEdge>minTreeInput.
I have a minimum spanning tree created from this graph, stored in std::vector<MinTreeEdge>minTreeOutput.
I'm trying to add a randomly add a certain number of edges back into minTreeOutput. To do this, I want to copy elements from minTreeInput back into minTreeOutput until the latter contains the required number of edges. Of course, each edge object that is copied over must not already be stored minTreeOutput. Can't have duplicate edges in this graph.
Below is what I've come up with so far. It works, but it's really long and I know the loop will have to be run many times depending on the graph and tree. I'd like to know how to do this properly:
// Edge class
struct MinTreeEdge
{
// For std::unique() between objects
bool operator==(MinTreeEdge const &rhs) const noexcept
{
return lhs == rhs.lhs;
}
int lhs;
int node1ID;
int node2ID;
int weight;
......
};
......
// The usage
int currentSize = minTreeOutput.size();
int targetSize = currentSize + numberOfEdgesToReturn;
int sizeDistance = targetSize - currentSize;
while(sizeDistance != 0)
{
//Probably really inefficient
for(std::vector<MinTreeEdge>::iterator it = minTreeInput.begin(); it != minTreeInput.begin()+sizeDistance; ++it)
minTreeOutput.push_back(*it);
std::vector<MinTreeEdge>::iterator mto_it;
mto_it = std::unique (minTreeOutput.begin(), minTreeOutput.end());
currentSize = minTreeOutput.size();
sizeDistance = targetSize - currentSize;
}
Alternatively, is there a way to just list all the edges in minTreeInput (graph) that are not in minTreeOutput(tree) without having to check each individual element in the former against the latter?
How can I efficiently copy objects (or a range of objects) from vector A into vector B, where vector B already contains certain objects identical to those from vector A, so that no objects copied from vector A are already listed in vector B?
If I understand the question correctly, this can be paraphrased to "how can I create a set union of two vectors?".
Answer: with std::set_union
set_union where MinTreeEdge is cheap to copy
Note that for this to work it requires that the two vectors are sorted. This is for efficiency reasons, as you have already touched upon.
#include <vector>
#include <algorithm>
#include <cassert>
#include <iterator>
struct MinTreeEdge
{
// For std::unique() between objects
bool operator==(MinTreeEdge const &rhs) const noexcept
{
return lhs == rhs.lhs;
}
int lhs;
int node1ID;
int node2ID;
int weight;
};
struct lower_lhs
{
bool operator()(const MinTreeEdge& l, const MinTreeEdge& r) const noexcept
{
return l.lhs < r.lhs;
}
};
std::vector<MinTreeEdge> merge(std::vector<MinTreeEdge> a,
std::vector<MinTreeEdge> b)
{
// let's pessimistically assume that the inputs are not sorted
// we could simply assert that they are if the caller is aware of
// the requirement
std::sort(a.begin(), a.end(), lower_lhs());
std::sort(b.begin(), b.end(), lower_lhs());
// alternatively...
// assert(std::is_sorted(a.begin(), a.end(), lower_lhs()));
// assert(std::is_sorted(b.begin(), b.end(), lower_lhs()));
// optional step if the inputs are not already `unique`
a.erase(std::unique(a.begin(), a.end()), a.end());
b.erase(std::unique(b.begin(), b.end()), b.end());
std::vector<MinTreeEdge> result;
result.reserve(a.size() + b.size());
std::set_union(a.begin(), a.end(),
b.begin(), b.end(),
std::back_inserter(result),
lower_lhs());
return result;
}
int main()
{
// example use case
auto a = std::vector<MinTreeEdge>{};
auto b = std::vector<MinTreeEdge>{};
b = merge(std::move(a), std::move(b));
}
set_union where MinTreeEdge is expensive to copy
There has been some mention of sets to accomplish this. And it is fair to say that if:
MinTreeEdge is expensive to copy and,
there are a great many of them
then we could expect to see a performance benefit in using an unordered_set. However, if the objects are expensive to copy then we would probably want to store them in our temporary set by reference.
I might do it this way:
// utility class which converts unary and binary operations on
// a reference_wrapper into unary and binary operations on the
// referred-to objects
template<class unary, class binary>
struct reference_as_object
{
template<class U>
decltype(auto) operator()(const std::reference_wrapper<U>& l) const {
return _unary(l.get());
}
template<class U, class V>
decltype(auto) operator()(const std::reference_wrapper<U>& l,
const std::reference_wrapper<V>& r) const {
return _binary(l.get(), r.get());
}
unary _unary;
binary _binary;
};
// utility to help prevent typos when defining a set of references
template<class K, class H, class C> using unordered_reference_set =
std::unordered_set<
std::reference_wrapper<K>,
reference_as_object<H, C>,
reference_as_object<H, C>
>;
// define unary and binary operations for our set. This way we can
// avoid polluting MinTreeEdge with artificial relational operators
struct mte_hash
{
std::size_t operator()(const MinTreeEdge& mte) const
{
return std::hash<int>()(mte.lhs);
}
};
struct mte_equal
{
bool operator()(MinTreeEdge const& l, MinTreeEdge const& r) const
{
return l.lhs == r.lhs;
}
};
// merge function. arguments by value since we will be moving
// *expensive to copy* objects out of them, and the vectors themselves
// can be *moved* into our function very cheaply
std::vector<MinTreeEdge> merge2(std::vector<MinTreeEdge> a,
std::vector<MinTreeEdge> b)
{
using temp_map_type = unordered_reference_set<MinTreeEdge, mte_hash, mte_equal>;
// build a set of references to existing objects in b
temp_map_type tmap;
tmap.reserve(b.capacity());
// b first, since the requirements mentioned 'already in B'
for (auto& ob : b) { tmap.insert(ob); }
// now add missing references in a
for (auto& oa : a) { tmap.insert(oa); }
// now build the result, moving objects from a and b as required
std::vector<MinTreeEdge> result;
result.reserve(tmap.size());
for (auto r : tmap) {
result.push_back(std::move(r.get()));
}
return result;
// a and b now have elements which are valid but in an undefined state
// The elements which are defined are the duplicates we don't need
// on summary, they are of no use to us so we drop them.
}
Trimmings - MinTreeEdge is expensive to copy but very cheap to move
Let's say that we wanted to stick with the vector method (we almost always should), but that MinTreeEdge was a little expensive to copy. Say it uses a pimpl idiom for internal polymorphism which will inevitably mean a memory allocation on copy. But let's say that it's cheaply moveable. Let's also imagine that the caller cannot be expected to sort or uniqueify data before sending it to us.
We can still achieve good efficiency with standard algorithms and vectors:
std::vector<MinTreeEdge> merge(std::vector<MinTreeEdge> a,
std::vector<MinTreeEdge> b)
{
// sorts a range if not already sorted
// #return a reference to the range
auto maybe_sort = [] (auto& c) -> decltype(auto)
{
auto begin = std::begin(c);
auto end = std::end(c);
if (not std::is_sorted(begin, end, lower_lhs()))
std::sort(begin, end, lower_lhs());
return c;
};
// uniqueify a range, returning the new 'end' of
// valid data
// #pre c is sorted
// #return result of std::unique(...)
auto unique = [](auto& c) -> decltype(auto)
{
auto begin = std::begin(c);
auto end = std::end(c);
return std::unique(begin, end);
};
// turn an iterator into a move-iterator
auto mm = [](auto iter) { return std::make_move_iterator(iter); };
std::vector<MinTreeEdge> result;
result.reserve(a.size() + b.size());
// create a set_union from two input containers.
// #post a and b shall be in a valid but undefined state
std::set_union(mm(a.begin()), mm(unique(maybe_sort(a))),
mm(b.begin()), mm(unique(maybe_sort(b))),
std::back_inserter(result),
lower_lhs());
return result;
}
If one provides a free function void swap(MinTreeEdge& l, MinTreeEdge& r) nothrow then this function will require exactly N moves, where N is the size of the result set. Since in a pimpl class, a move is simply a pointer swap, this algorithm remains efficient.
Since your output vector should not contain duplicates, one way to accomplish not storing duplicates is to change the output container to a std::set<MinEdgeTree> instead of std::vector<MinEdgeTree>. The reason is that a std::set does not store duplicates, thus you do not have to write the code to do this check yourself.
First, you need to define an operator < for your MinEdgeTree class:
struct MinTreeEdge
{
// For std::unique() between objects
bool operator==(MinTreeEdge const &rhs) const noexcept
{
return lhs == rhs.lhs;
}
// For std::unique() between objects
bool operator<(MinTreeEdge const &rhs) const noexcept
{
return lhs < rhs.lhs;
}
//...
};
Once you do that, the while loop can be replaced with the following:
#include <set>
#include <vector>
#include <iterator>
#include <algorithm>
//...
std::vector<MinTreeEdge> minTreeInput;
//...
std::set<MinTreeEdge> minTreeOutput;
//...
std::copy(minTreeInput.begin(), minTreeInput.end(),
std::inserter(minTreeOutput, minTreeOutput.begin()));
There is no need to call std::unique at all, since it is the std::set that will check for the duplicates.
If the output container has to stay as a std::vector, you can still do the above using a temporary std::set and then copy the std::set to the output vector:
std::vector<MinTreeEdge> minTreeInput;
std::vector<MinTreeEdge> minTreeOutput;
//...
std::set<MinTreeEdge> tempSet;
std::copy(minTreeInput.begin(), minTreeInput.end(),
std::inserter(tempSet, tempSet.begin()));
std::copy(tempSet.begin(), tempSet.end(),std::back_inserter(minTreeOutput));
You may use the following:
struct MinTreeEdge
{
bool operator<(MinTreeEdge const &rhs) const noexcept
{
return id < rhs.id;
}
int id;
int node1ID;
int node2ID;
int weight;
};
std::vector<MinTreeEdge> CreateRandomGraph(const std::vector<MinTreeEdge>& minSpanningTree,
const std::vector<MinTreeEdge>& wholeTree,
std::mt19937& rndEng,
std::size_t expectedSize)
{
assert(std::is_sorted(minSpanningTree.begin(), minSpanningTree.end()));
assert(std::is_sorted(wholeTree.begin(), wholeTree.end()));
assert(minSpanningTree.size() <= expectedSize);
assert(expectedSize <= wholeTree.size());
std::vector<MinTreeEdge> res;
std::set_difference(wholeTree.begin(), wholeTree.end(),
minSpanningTree.begin(), minSpanningTree.end(),
std::back_inserter(res));
std::shuffle(res.begin(), res.end(), rndEng);
res.resize(expectedSize - minSpanningTree.size());
res.insert(res.end(), minSpanningTree.begin(), minSpanningTree.end());
// std::sort(res.begin(), res.end());
return res;
}
I have a C++ class that acts like a container: it has size() and operator[] member functions. The values stored "in" the container are std::tuple objects. However, the container doesn't actually hold the tuples in memory; instead, it constructs them on-demand based on underlying data stored in a different form.
std::tuple<int, int, int>
MyContainer::operator[](std::size_t n) const {
// Example: draw corresponding elements from parallel arrays
return { underlying_data_a[n], underlying_data_b[n], underlying_data_c[n] };
}
Hence, the return type of operator[] is a temporary object, not a reference. (This means it's not an lvalue, so the container is read-only; that's OK.)
Now I'm writing an iterator class that can be used to traverse the tuples in this container. I'd like to model RandomAccessIterator, which depends on InputIterator, but InputIterator requires support for the expression i->m (where i is an iterator instance), and as far as I can tell, an operator-> function is required to return a pointer.
Naturally, I can't return a pointer to a temporary tuple that's constructed on-demand. One possibility that comes to mind is to put a tuple instance into the iterator as a member variable, and use it to store a copy of whichever value the iterator is currently positioned on:
class Iterator {
private:
MyContainer *container;
std::size_t current_index;
// Copy of (*container)[current_index]
std::tuple<int, int, int> current_value;
// ...
};
However, updating the stored value will require the iterator to check whether its current index is less than the container's size, so that a past-the-end iterator doesn't cause undefined behavior by accessing past the end of the underlying arrays. That adds (a small amount of) runtime overhead — not enough to make the solution impractical, of course, but it feels a little inelegant. The iterator shouldn't really need to store anything but a pointer to the container it's iterating and the current position within it.
Is there a clean, well-established way to support operator-> for iterator types that construct their values on-demand? How would other developers do this sort of thing?
(Note that I don't really need to support operator-> at all — I'm implementing the iterator mainly so that the container can be traversed with a C++11 "range for" loop, and std::tuple doesn't have any members that one would typically want to access via -> anyway. But I'd like to model the iterator concepts properly nonetheless; it feels like I'm cutting corners otherwise. Or should I just not bother?)
template<class T>
struct pseudo_ptr {
T t;
T operator*()&&{return t;}
T* operator->(){ return &t; }
};
then
struct bar { int x,y; };
struct bar_iterator:std::iterator< blah, blah >{
// ...
pseudo_ptr<bar> operator->() const { return {**this}; }
// ...
};
This relies on how -> works.
ptr->b for pointer ptr is simply (*ptr).b.
Otherwise it is defined as (ptr.operator->())->b. This evaluates recursively if operator-> does not return a pointer.
The pseudo_ptr<T> above gives you a wrapper around a copy of T.
Note, however, that lifetime extension doesn't really work. The result is fragile.
Here's an example relying on the fact that operator-> is applied repeatedly until a pointer is returned. We make Iterator::operator-> return the Contained object as a temporary. This causes the compiler to reapply operator->. We then make Contained::operator-> simply return a pointer to itself. Note that if we don't want to put operator-> in the Contained on-the-fly object, we can wrap it in a helper object that returns a pointer to the internal Contained object.
#include <cstddef>
#include <iostream>
class Contained {
public:
Contained(int a_, int b_) : a(a_), b(b_) {}
const Contained *operator->() {
return this;
}
const int a, b;
};
class MyContainer {
public:
class Iterator {
friend class MyContainer;
public:
friend bool operator!=(const Iterator &it1, const Iterator &it2) {
return it1.current_index != it2.current_index;
}
private:
Iterator(const MyContainer *c, std::size_t ind) : container(c), current_index(ind) {}
public:
Iterator &operator++() {
++current_index;
return *this;
}
// -> is reapplied, since this returns a non-pointer.
Contained operator->() {
return Contained(container->underlying_data_a[current_index], container->underlying_data_b[current_index]);
}
Contained operator*() {
return Contained(container->underlying_data_a[current_index], container->underlying_data_b[current_index]);
}
private:
const MyContainer *const container;
std::size_t current_index;
};
public:
MyContainer() {
for (int i = 0; i < 10; i++) {
underlying_data_a[i] = underlying_data_b[i] = i;
}
}
Iterator begin() const {
return Iterator(this, 0);
}
Iterator end() const {
return Iterator(this, 10);
}
private:
int underlying_data_a[10];
int underlying_data_b[10];
};
int
main() {
MyContainer c;
for (const auto &e : c) {
std::cout << e.a << ", " << e.b << std::endl;
}
}
I am writing a template Polynom<T> class where T is the numeric type of its coefficients.
The coefficients of the polynom are stored in an std::vector<T> coefficients, where coefficients[i] corresponds to x^i in a real polynom. (so the powers of x are in increasing order).
It is guaranteed that coefficients vector always contains at least one element. - for a zero polynom it is T().
I want to overload the operator[] to do the following:
The index passed to the operator[] corresponds to the power of X whose coefficient we want to modify / read.
If the user wants to just read the coefficient, it should throw for negative indices, return coefficients.at(i) for indices within the stored range - and reasonably return 0 for all other indices, not throw.
If the user wants to modify the coefficient, it should throw for negative indices, but let user modify all other indices freely, even if the index specified is bigger than or equal to coefficients.size(). So we want to somehow resize the vector.
The main problem I have collided with is as follows:
1.
How do I distinguish between the read case and the write case? One person left me without an explanation but said that writing two versions:
const T& operator[] (int index) const;
T& operator[] (int index);
was insufficient. However, I thought that the compiler would prefer the const version in the read case, won't it?
2.
I want to make sure that no trailing zeros are ever stored in the coefficients vector. So I somehow have to know in advance, "before" I return a mutable T& of my coefficient, what value user wants to assign. And I know that operator[] doesn't receive a second argument.
Obviously, if this value is not zero (not T()), then I have to resize my vector and set the appropriate coefficient to the value passed.
But I cannot do it in advance (before returning a T& from operator[]), because if the value to be assigned is T(), then, provided I resize my coefficients vector in advance, it will eventually have lots of trailing "zeroes".
Of course I can check for trailing zeroes in every other function of the class and remove them in that case. Seems a very weird decision to me, and I want every function to start working in assumption that there are no zeroes at the end of the vector if its size > 1.
Could you please advise me as concrete solution as possible to this problem?
I heard something about writing an inner class implicitly convertible to T& with overloaded operator=, but I lack the details.
Thank you very much in advance!
One option you could try (I haven't tested this):
template<typename T>
class MyRef{
private:
int index;
Polynom<T>*p;
public:
MyRef(int index, Polynom<T>*p) : index(index), p(p) { }
MyRef<T>& operator=(T const&t); //and define these appropriately
T operator T() const;
};
and define:
MyRef<T> operator[](int index){
return MyRef<T>(index, this);
}
This way when you assign a value to the "reference" it should have access to all the needed data in the polynomial, and take the appropriate actions.
I am not familiar enough with your implementation, so I'll instead give an example of a very simple dynamic array that works as follows:
you can read from any int index without concern; elements not previously written to should read off as 0;
when you write to an element past the end of the currently allocated array, it is reallocated, and the newly allocated elements are initialized to 0.
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T>
class my_array{
private:
T* _data;
int _size;
class my_ref{
private:
int index;
T*& obj;
int&size;
public:
my_ref(T*& obj, int&size, int index)
: index(index), obj(obj), size(size){}
my_ref& operator=(T const& t){
if (index>=size){
obj = (T*)realloc(obj, sizeof(T)*(index+1) );
while (size<=index)
obj[size++]=0;
}
obj[index] = t;
return *this;
}
//edit:this one should allow writing, say, v[1]=v[2]=v[3]=4;
my_ref& operator=(const my_ref&r){
operator=( (T) r);
return *this;
}
operator T() const{
return (index>=size)?0:obj[index];
}
};
public:
my_array() : _data(NULL), _size(0) {}
my_ref operator[](int index){
return my_ref(_data,_size,index);
}
int size() const{ return _size; }
};
int main(){
my_array<int> v;
v[0] = 42;
v[1] = 51;
v[5] = 5; v[5]=6;
v[30] = 18;
v[2] = v[1]+v[5];
v[4] = v[8]+v[1048576]+v[5]+1000;
cout << "allocated elements: " << v.size() << endl;
for (int i=0;i<31;i++)
cout << v[i] << " " << endl;
return 0;
}
It's a very simple example and not very efficient in its current form but it should prove the point.
Eventually you might want to overload operator& to allow things like *(&v[0] + 5) = 42; to work properly. For this example, you could have that operator& gives a my_pointer which defines operator+ to do arithmetic on its index field and return a new my_pointer. Finally, you can overload operator*() to go back to a my_ref.
The solution to this is a proxy class (untested code follows):
template<typename T> class Polynom
{
public:
class IndexProxy;
friend class IndexProxy;
IndexProxy operator[](int);
T operator[](int) const;
// ...
private:
std::vector<T> coefficients;
};
template<typename T> class Polynom<T>::IndexProxy
{
public:
friend class Polynom<T>;
// contrary to convention this assignment does not return an lvalue,
// in order to be able to avoid extending the vector on assignment of 0.0
T operator=(T const& t)
{
if (theIndex >= thePolynom.coefficients.size())
thePolynom.coefficients.resize(theIndex+1);
thePolynom.coefficients[theIndex] = t;
// the assignment might have made the polynom shorter
// by assigning 0 to the top-most coefficient
while (thePolynom.coefficients.back() == T())
thePolynom.coefficients.pop_back();
return t;
}
operator T() const
{
if (theIndex >= thePolynom.coefficients.size())
return 0;
return thePolynom.coefficients[theIndex];
}
private:
IndexProxy(Polynom<T>& p, int i): thePolynom(p), theIndex(i) {}
Polynom<T>& thePolynom;
int theIndex;
}
template<typename T>
Polynom<T>::IndexProxy operator[](int i)
{
if (i < 0) throw whatever;
return IndexProxy(*this, i);
}
template<typename T>
T operator[](int i)
{
if (i<0) throw whatever;
if (i >= coefficients.size()) return T();
return coefficients[i];
}
Obviously the code above is not optimized (especially the assignment operator has clearly room for optimization).
You cannot distinguish between read and write with operator overloads. The best you can do is distinguish between usage in a const setting and a non-const setting, which is what your code snippet does. So:
Polynomial &poly = ...;
poly[i] = 10; // Calls non-const version
int x = poly[i]; // Calls non-const version
const Polynomial &poly = ...;
poly[i] = 10; // Compiler error!
int x = poly[i] // Calls const version
It sounds like the answer to both your questions, therefore, is to have separate set and get functions.
I see two solutions to your problem:
Instead of storing the coefficients in a std::vector<T> store them in a std::map<unsigned int, T>. This way you will ever only store non-zero coefficients. You could create your own std::map-based container that would consume zeros stored into it. This way you also save some storage for polynomials of the form x^n with large n.
Add an inner class that will store an index (power) and coefficient value. You would return a reference to an instance of this inner class from operator[]. The inner class would overwrite operator=. In the overridden operator= you would take the index (power) and coefficient stored in inner class instance and flush them to the std::vector where you store your coefficients.
This is not possible. The only way I can think of is to provide a special member-function for adding new coefficients.
The compiler decides between the const and non-const version by looking at the type of Polynom, and not by checking what kind of operation is performed on the return-value.