Maybe overkill, but then again maybe not - template expansion can write conversion functions for you.
This template class allows you to specify which elems should be valid for the vector, and in which order.
It also provides the ability to index into the vector by elem, either dynamically (throw exception if does not exists) or statically (fail to compile if out of bounds).
#include <array>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <stdexcept>
//
// boilerplate
//
namespace notstd {
template<class T, class Tuple>
struct tuple_index;
template<class T, class... Types>
struct tuple_index<T, std::tuple<T, Types...>> {
using type = std::size_t;
static const type value = 0;
};
template<class T, class U, class... Types>
struct tuple_index<T, std::tuple<U, Types...>> {
using type = std::size_t;
static const type value = 1 + tuple_index<T, std::tuple<Types...>>::value;
};
}
enum element_type {
elem0, elem1, elem2, elem3, elem4, elem5, elem1000
};
template<element_type Value>
struct where_type
{
static const element_type value = Value;
};
template<element_type Value>
static constexpr auto where = where_type<Value> {};
template<element_type...Permitted>
struct restricted_vector : private std::array<double, sizeof...(Permitted)> {
using corresponding_tuple = std::tuple<where_type<Permitted>...>;
using inherited = std::array<double, sizeof...(Permitted)>;
using inherited::inherited;
static auto conversion(element_type e) -> std::size_t
{
static const std::array<std::pair<element_type, std::size_t>, sizeof...(Permitted)> a = {
std::make_pair(Permitted, notstd::tuple_index<where_type<Permitted>, corresponding_tuple>::value)...
};
auto ifind = std::find_if(a.begin(), a.end(), [e](auto&& elem) { return elem.first == e; });
if (ifind == a.end()) {
throw std::out_of_range("invalid element");
}
return ifind->second;
}
template<element_type Elem>
auto& operator[](where_type<Elem>) {
auto pos = notstd::tuple_index<where_type<Elem>, corresponding_tuple >::value;
return inherited::operator[](pos);
}
template<element_type Elem>
auto const& operator[](where_type<Elem>) const {
auto pos = notstd::tuple_index<where_type<Elem>, corresponding_tuple >::value;
return inherited::operator[](pos);
}
// dynamic access
auto& at(element_type e) {
return inherited::operator[](conversion(e));
}
auto const& at(element_type e) const {
return inherited::operator[](conversion(e));
}
using inherited::begin;
using inherited::end;
using inherited::size; // etc
};
int main() {
auto v1 = restricted_vector<elem3, elem4, elem5> {};
v1[where<elem4>] = 0.4;
v1[where<elem5>] = 0.5;
v1[where<elem3>] = 0.3;
std::copy(v1.begin(), v1.end(), std::ostream_iterator<double>(std::cout, ", "));
std::cout << "\n";
std::cout << "at elem4: " << v1.at(elem4) << std::endl;
}
expected output:
0.3, 0.4, 0.5,
at elem4: 0.4