I am working on a family of generalized container wrappers and sequence generators that provide the classic sequence facilities such as integer range, mapping of a function to a sequence, trimming or reversing of a sequence, etc ..
Each class wraps a sequence and exposes a modified iterator. They all have a structure similar to the boxA and boxB in the attached code.
The difference between BoxA and BoxB is that BoxA stores a const & to the underlying container or generator while BoxB stores a non-const reference.
My first question is why is it possible to use nested expressions with the const version, while it is necessary to store the output of calls to the boxB constructor in a variable
1 2 3 4
|
auto x = make_boxA(make_boxA(v)); //this is not possible with BoxB;
auto y1 = boxB(v); //this what is required with boxB
auto y2 = boxB(y1);
| |
Secondly, I really like the aesthetics and simplicity of the nested expressions; is there a way to make them possible while avoiding the const’ing of the underlying container reference?
The problem when the underlying container is const’ed is that ... it is const'ed and the class loses the ability to expose direct references to the content of the underlying object. For example operator [] and the dereferenced iterator can’t be used as an l-value. Obviously, such an assignment doesn't always make sense depending on the type of wrapped sequence/container but that can be handled effectively through template parameters.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
#include <iostream>
#include <vector>
template < class T > class boxA
{
public:
const T & v;
typedef decltype(v[std::declval<int>()]) R;
typedef decltype(v.begin()) I;
boxA (const T & v):v (v) {}
R operator[] (int i) const{return v[i];}
I begin() const {return v.begin();}
I end() const {return v.end();}
};
template < class T >
boxA < T > make_boxA (const T & b)
{return boxA < T > (b);}
template < class T > class boxB
{
public:
T & v;
typedef decltype(v[std::declval<int>()]) R;
typedef decltype(v.begin()) I;
boxB ( T & v):v (v) {}
R operator[] (int i) const {return v[i];}
R & operator[] (int i) {return v[i];}
I begin(){return v.begin();}
I end(){return v.end();}
};
template < class T >
boxB < T > make_boxB ( T & b)
{return boxB < T > (b);}
int main ()
{
std::vector < int >v = { 1, 2, 3, 4, 5, 6 };
//with boxA
for(auto i : make_boxA (make_boxA (v))){
std::cout << i <<", "; }
std::cout << std::endl;
//with boxB
auto c = make_boxB (v);
auto d = make_boxB (c);
d[3] = 99;
for(auto i : d){std::cout << i <<", "; }
std::cout << std::endl;
}
| |