I have this very simple template:
#include <algorithm>
#include <initializer_list>
#include <iostream>
#include <iterator>
#include <list>
#include <set>
#include <vector>
template<class Container>
class ImmutableContainerView {
private:
Container& my_container;
public:
ImmutableContainerView(Container& container) :
my_container {container} {}
typename Container::const_iterator cbegin() const {
return my_container.cbegin();
}
typename Container::const_iterator cend() const {
return my_container.cend();
}
typename Container::const_reverse_iterator crbegin() const {
return my_container.crbegin();
}
typename Container::const_reverse_iterator crend() const {
return my_container.crend();
}
typename Container::size_type size() const {
return my_container.size();
}
bool empty() const {
return my_container.empty();
}
};
int main(int argc, const char * argv[]) {
std::initializer_list<int> il = { 1, 2, 4, 5, 6 };
std::list<int> my_list = il;
std::set<int> my_set = il;
std::vector<int> my_vector = il;
ImmutableContainerView<std::list<int>> my_immutable_list(my_list);
ImmutableContainerView<std::set<int>> my_immutable_set(my_set);
ImmutableContainerView<std::vector<int>> my_immutable_vector(my_vector);
// Iterators:
std::ostream_iterator<int> out_it (std::cout, ", ");
std::copy(my_immutable_list.cbegin(), my_immutable_list.cend(), out_it);
std::cout << "\n";
std::copy(my_immutable_set.cbegin(), my_immutable_set.cend(), out_it);
std::cout << "\n";
std::copy(my_immutable_vector.cbegin(), my_immutable_vector.cend(), out_it);
std::cout << "\n";
// Reversed iterators:
std::copy(my_immutable_list.crbegin(), my_immutable_list.crend(), out_it);
std::cout << "\n";
std::copy(my_immutable_set.crbegin(), my_immutable_set.crend(), out_it);
std::cout << "\n";
std::copy(my_immutable_vector.crbegin(), my_immutable_vector.crend(), out_it);
std::cout << "\n";
// Size:
std::cout << my_immutable_list.size() << "\n";
std::cout << my_immutable_set.size() << "\n";
std::cout << my_immutable_vector.size() << "\n";
// Empty:
std::cout << std::boolalpha;
std::cout << my_immutable_list.empty() << "\n";
std::cout << my_immutable_set.empty() << "\n";
std::cout << my_immutable_vector.empty() << "\n";
return 0;
}
I would like to hear comments on how to improve it. In particular, is it possible to do some metaprogramming trickery such that it can adapt to different containers; for example, it could provide operator[size_t]
for std::vector
, but find
for std::set
?
1 Answer 1
Your view isn't immutable, though immutability might be a property of the underlying object. It's a constant view, meaning the underlying object might change, but not from that view. The canonical way to get a constant view is by the way to use a constant reference, unless the view should erase some of the differences between underlying containers, like std::string_view
does.
You are missing .begin()
, .end()
, .rbegin()
and .rend()
.
Your view is constant, fine, but forcing everyone to explicitly say so for each function-call breaks interface conventions.
The missing functions are the common interface, the ones you provided just optional convenience-functions for calling manually which might not be provided even if possible, and certainly never used in generic code. Take a look at <iterator>
and the free functions for accessing any generic range there.