operator[]
must always return the same type for a given overload. The only way one overload is selected over another is via parameter types, so in this case as the parameter type is always an integer, there is only one overload. Ambigious overloads are not permitted.
My method relies on fully constexpr data and plays around with the return type instead of the operator[]
. I've done this within a struct, with static constexpr data members.
Templated versions will stamp out a function for each type presented as a parameter.
Yes you can use this code to get a tuple value at a specific index but your use cases are narrowed to within the context that is shown in the example below.
This is how its used:
struct test
{
static constexpr array_operator_tuple<int, bool, unsigned> vals{ 1, true, 10u };
//
static constexpr auto r0 = vals[0];
//
// Equality operator
static_assert( r0 == 2, "The item required is not valid or not active." ); // Error as expected.
static_assert( r0 == 1, "The item required is not valid or not active." ); // No error as expected.
static_assert( r0 == true, "The item required is not valid or not active." ); // Error as expected.
static_assert( r0 == false, "The item required is not valid or not active." ); // Error as expected.
static_assert( r0 == 2u, "The item required is not valid or not active." ); // Error as expected.
static_assert( r0 == 10u, "The item required is not valid or not active." ); // Error as expected.
//
// Invalidity operator.
static_assert( r0 > 10u, "The item required is not valid or not active." ); // Error as expected.
static_assert( r0 <= 1u, "The item required is not valid or not active." ); // No error as expected.
static_assert( r0 < 9u, "The item required is not valid or not active." ); // No error as expected.
};
Code can be played with here.
#include <tuple>
#include <iostream>
template <typename T>
struct magic_item
{
T const * value = nullptr;
//
constexpr magic_item(T const * ptr) noexcept : value{ptr} {}
//
constexpr bool is_active() const noexcept
{
return value != nullptr;
}
constexpr bool is_value( T const & v ) const noexcept
{
return *value == v;
}
};
template <typename ... Args>
struct magic_tuple : std::tuple<magic_item<Args>...>
{
static constexpr size_t count = sizeof...(Args);
//
constexpr magic_tuple(Args const * ... args) noexcept :
std::tuple<magic_item<Args>...>{ {args}... }
{}
private:
template <size_t ... I>
constexpr bool active_index_impl(std::index_sequence<I...>) const noexcept
{
size_t output = ~static_cast<size_t>(0);
(((std::get<I>(*this) != nullptr) and (output = I, true)) or ...);
return output;
}
//
template <size_t ... I>
constexpr bool is_active_impl(size_t index, std::index_sequence<I...>) const noexcept
{
return (((index == I) and std::get<I>(*this).is_active()) or ...);
}
public:
constexpr bool is_active(size_t index) const noexcept
{
return is_active_impl(index, std::make_index_sequence<count>());
}
constexpr size_t active_index() const noexcept
{
return active_index_impl(std::make_index_sequence<count>());
}
//
template <typename T>
constexpr bool operator == (T const & value) const noexcept
{
using type = std::remove_cv_t<std::decay_t<T>>;
return std::get<magic_item<type>>(*this).is_active() and (*std::get<magic_item<type>>(*this).value == value);
}
template <typename T>
constexpr bool operator <= (T const & value) const noexcept
{
using type = std::remove_cv_t<std::decay_t<T>>;
return std::get<magic_item<type>>(*this).is_active() and (*std::get<magic_item<type>>(*this).value <= value);
}
template <typename T>
constexpr bool operator >= (T const & value) const noexcept
{
using type = std::remove_cv_t<std::decay_t<T>>;
return std::get<magic_item<type>>(*this).is_active() and (*std::get<magic_item<type>>(*this).value >= value);
}
template <typename T>
constexpr bool operator < (T const & value) const noexcept
{
using type = std::remove_cv_t<std::decay_t<T>>;
return std::get<magic_item<type>>(*this).is_active() and (*std::get<magic_item<type>>(*this).value < value);
}
template <typename T>
constexpr bool operator > (T const & value) const noexcept
{
using type = std::remove_cv_t<std::decay_t<T>>;
return std::get<magic_item<type>>(*this).is_active() and (*std::get<magic_item<type>>(*this).value > value);
}
};
//
template <typename ... Args, size_t ... I>
constexpr auto get_impl(size_t index, std::tuple<Args...> const & tup, std::index_sequence<I...>) -> magic_tuple< Args ... >
{
return magic_tuple< Args ... >{ ((index == I) ? &std::get<I>(tup) : nullptr ) ... };
}
template <typename ... Args>
constexpr auto get(size_t index, std::tuple<Args...> const & tup)
{
return get_impl(index, tup, std::make_index_sequence<sizeof...(Args)>{} );
}
//
template <typename ... Args>
struct array_operator_tuple : std::tuple<Args...>
{
using base_t= std::tuple<Args...>;
using base_t::base_t;
//
constexpr auto operator[](size_t index) const noexcept
{
return get(index, *this);
}
};
//
struct test
{
static constexpr array_operator_tuple<int, bool, unsigned> vals{ 1, true, 10u };
//
static constexpr auto r0 = vals[0];
//
static_assert( r0 == 2, "The item required is not valid or not active." ); // Error as expected.
static_assert( r0 == 1, "The item required is not valid or not active." ); // No error as expected.
static_assert( r0 == true, "The item required is not valid or not active." ); // Error as expected.
static_assert( r0 == false, "The item required is not valid or not active." ); // Error as expected.
static_assert( r0 == 2u, "The item required is not valid or not active." ); // Error as expected.
static_assert( r0 == 10u, "The item required is not valid or not active." ); // Error as expected.
//
static constexpr auto r1 = vals[1];
//
static_assert( r1 == 2, "The item required is not valid or not active." ); // Error as expected.
static_assert( r1 == 1, "The item required is not valid or not active." ); // Error as expected.
static_assert( r1 == true, "The item required is not valid or not active." ); // No error as expected.
static_assert( r1 == false, "The item required is not valid or not active." ); // Error as expected.
static_assert( r1 == 2u, "The item required is not valid or not active." ); // Error as expected.
static_assert( r1 == 10u, "The item required is not valid or not active." ); // Error as expected.
//
static constexpr auto r2 = vals[2];
//
static_assert( r2 == 2, "The item required is not valid or not active." ); // Error as expected.
static_assert( r2 == 1, "The item required is not valid or not active." ); // Error as expected.
static_assert( r2 == true, "The item required is not valid or not active." ); // Error as expected.
static_assert( r2 == false, "The item required is not valid or not active." ); // Error as expected.
static_assert( r2 == 2u, "The item required is not valid or not active." ); // Error as expected.
static_assert( r2 == 10u, "The item required is not valid or not active." ); // No error as expected.
//
static_assert( r2 > 10u, "The item required is not valid or not active." ); // Error as expected.
static_assert( r2 >= 10u, "The item required is not valid or not active." ); // No error as expected.
static_assert( r2 > 9u, "The item required is not valid or not active." ); // No error as expected.
};
//
int main()
{
test a{};
return 0;
}
Its generally not good practice to derive from standard library, this is just demonstrating the algorithm.