Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile-time operator[]

Tags:

c++

c++17

How can I achieve a compile-time index operation wrapped in an operator like () or []?

// works, I already made this
template<int i>
constexpr auto get() const
{
    // implementation
    // where i is used as a template parameter to other things
}

// no idea how to achieve this
template</*magic*/>
constexpr auto operator[](/*more magic*/) const
{
    return get</*use magic*/>();
}

Usage

constexpr my_class x;
...= x.get<1>(); // works, kind of ugly
...= x[1]; // doesn't work, parameters aren't compiletime or something

Here's an example I slapped together. Hopefully the solution to this example will be the same solution to my real problem.

#include <tuple>

class c
{
    std::tuple< int, float, char > tuple { 1, 2.f, 'c' };

public:
    template< std::size_t i >
    constexpr auto & get()
    {
        return std::get<i>(tuple);
    }
    //constexpr auto & operator[](std::size_t i)
    //{
    //    return std::get<i>(tuple);
    //}
};

int main()
{
    constexpr c x;
    static_assert( x.get<2>() == 'c' );
    static_assert( x.get<1>() - 2.f < .1f );
    static_assert( x.get<0>() == 1 );
    //static_assert( x[2] == 'c' );
    //static_assert( x[1] - 2.f < .1f );
    //static_assert( x[0] == 1 );
}
like image 839
nowi Avatar asked Aug 10 '26 23:08

nowi


1 Answers

Your operator[] must always return the same type for a given parameter type. The way to work around this is to make each parameter a different type.

For example:

template <std::size_t I>
using IndexConstantT = std::integral_constant<std::size_t, I>;

template <std::size_t I>
constexpr IndexConstantT<I> IndexConstant;

class c
{
    std::tuple< int, float, char > tuple { 1, 2.f, 'c' };

public:
    template <std::size_t i>
    constexpr auto& operator[](IndexConstantT<i>) const
    {
        return std::get<i>(tuple);
    }
};

int main()
{
    constexpr const c x;
    static_assert( x[IndexConstant<2>] == 'c' );
    static_assert( x[IndexConstant<1>] - 2.f < .1f );
    static_assert( x[IndexConstant<0>] == 1 );
}

Live Demo


As suggested by @NicolBolas in the comments, to make the syntax a little nicer, you could use a User Defined Literal so that you can use just 2_ic instead of IndexConstant<2>:

constexpr std::size_t c_to_i(char c)
{
    return c - '0';
}

constexpr std::size_t constexpr_pow(std::size_t base, std::size_t exp)
{
    std::size_t ret = 1;
    for (std::size_t i = 0; i < exp; ++i) {
        ret *= base;
    }
    return ret;
}

template <char... Cs, std::size_t... Is>
constexpr std::size_t to_size_t_impl(std::index_sequence<Is...>)
{
    return ((c_to_i(Cs) * constexpr_pow(10, sizeof...(Is) - 1 - Is)) + ...);
}

template <char... Cs>
constexpr std::size_t to_size_t()
{
    return to_size_t_impl<Cs...>(std::make_index_sequence<sizeof...(Cs)>{});
}

template <char... Cs>
constexpr auto operator""_ic()
{
    return IndexConstant<to_size_t<Cs...>()>;
}

Live Demo

like image 199
Miles Budnek Avatar answered Aug 13 '26 13:08

Miles Budnek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!