Оператор времени компиляции [] - PullRequest
2 голосов
/ 02 апреля 2020

Как мне выполнить операцию индексации во время компиляции, заключенную в такой оператор, как () или []?

// 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*/>();
}

Использование

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

Вот пример, который я собрал вместе. Надеюсь, что решение этого примера будет таким же решением моей реальной проблемы.

#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 );
}

Ответы [ 2 ]

1 голос
/ 02 апреля 2020

Ваш operator[] всегда должен возвращать один и тот же тип для данного типа параметра. Чтобы обойти это, нужно сделать каждый параметр различным типом.

Например:

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


Как предложено @NicolBolas в комментариях, чтобы сделать синтаксис немного лучше, вы можете использовать User Defined Literal , чтобы вы могли использовать только 2_ic вместо 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...>()>;
}

Демонстрационная версия

0 голосов
/ 02 апреля 2020

operator[] всегда должен возвращать один и тот же тип для данной перегрузки. Единственный способ выбора одной перегрузки над другим - через типы параметров, поэтому в этом случае, поскольку тип параметра всегда целочисленный, существует только одна перегрузка. Не допускаются неоднозначные перегрузки.

Мой метод полностью использует данные constexpr и использует тип возвращаемого значения вместо operator[]. Я сделал это в структуре с использованием элементов данных stati c constexpr.

Шаблонные версии будут выделять функцию для каждого типа, представленного в качестве параметра.


Да, вы Можно использовать этот код для получения значения кортежа по указанному c индексу, но ваши варианты использования сужаются до контекста, который показан в примере ниже.

Вот как оно используется:

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.
};

Код можно воспроизвести с помощью здесь .

#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;
}

Обычно не рекомендуется выводить из стандартной библиотеки, это просто демонстрация алгоритма.

...