0
votes

What I want is illustrated as follows:

int array[4] { 1, 2, 3, 4 };
auto n1 = array[-1];
assert(4 == n1);
auto n2 = array[-2];
assert(3 == n2);

std::vector coll { 1, 2, 3, 4 };
auto n3 = coll[-1];
assert(4 == n3);
auto n4 = coll[-2];
assert(3 == n4);

I tried the following template function:

template<typename C, typename I>
constexpr decltype(auto) operator [](const C& coll, I idx)
{
    if (idx >= 0)
    {
        return coll[idx];
    }
    else
    {
        return coll[std::size(coll) + idx];
    }
}

But Clang complains:

error : overloaded 'operator[]' must be a non-static member function

constexpr decltype(auto) operator [](const C& coll, I idx)

Is it possible to correctly implement the function in modern C++?

2
You can't do it with operator[], which must be member function.songyuanyao
The condition should probably be if (idx >= 0). Otherwise x[0] will give you one element beyond the end of the array, which won't end well :-)paxdiablo
Question title & contents don't match. Please fix & flag this comment as obsolete.iammilind
Beware: std::size(coll) + idx will cause wrap-around in the range of size_t, due to the impractical return type of C++17 std::size.Cheers and hth. - Alf

2 Answers

2
votes

You can't overload operator[] for a raw array.

But you can just define some named functions, e.g., off the cuff:

using Index = ptrdiff_t;

template< class Item, size_t n >
auto item( Index const i, Item (&a)[n] )
    -> Item&
{ return (i < 0? a[Index( n ) + i] : a[i]); }

Then the test code for raw arrays, suitably adapted, would be like

int array[] { 1, 2, 3, 4 };
int n1 = item( -1, array );
assert( 4 == n1 );
int n2 = item( -2, array );
assert( 3 == n2 );

I leave a definition for a general collection like std::vector, as an exercise for the reader. :)

1
votes

You can also make above solution more generic (at least for standard sequence containers) with the use of non-member std::begin() functions. Example:

template <typename C>
auto item(C const& container, int index) -> decltype(*std::cbegin(container))
{
    return index >= 0 ?
        *(std::cbegin(container) + index) :
        *(std::crbegin(container) - index - 1);
}

And then use it like that:

std::vector<int> coll{ 1, 2, 3, 4 };
int arra[] { 10, 20, 30, 40 };

auto last = item(coll, -1);
auto secondLast = item(coll, -2);

last = item(arra, -1);
secondLast = item(arra, -2);

return 0;

Hope that helps :)