[英]Compile-time operator[]
如何實現包含在 () 或 [] 等運算符中的編譯時索引操作?
// 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 );
}
您的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 );
}
正如@NicolBolas 在評論中所建議的,為了使語法更好一點,您可以使用用戶定義的文字,以便您可以僅使用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...>()>;
}
operator[]
必須始終為給定的重載返回相同的類型。 選擇一個重載而不是另一個重載的唯一方法是通過參數類型,因此在這種情況下,由於參數類型始終是整數,因此只有一個重載。 不允許有歧義的重載。
我的方法依賴於完全 constexpr 數據並使用返回類型而不是operator[]
。 我已經在一個結構中完成了這個,使用靜態 constexpr 數據成員。
模板版本將為作為參數呈現的每種類型標記一個函數。
是的,您可以使用此代碼獲取特定索引處的元組值,但您的用例已縮小到以下示例中顯示的上下文內。
這是它的使用方式:
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;
}
從標准庫派生通常不是一個好習慣,這只是演示算法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.