Я обнаружил, что мне нужно перебирать (обычно) массив или кортеж из N T.
Код демо:
http://coliru.stacked-crooked.com/a/9b8ab7c01b79d086
Я придумал следующую реализацию, которой я вполне доволен, так как она, кажется, справляется с нужными мне случаями без каких-либо накладных расходов.
#include <array>
#include <iostream>
#include <tuple>
#include <vector>
namespace functional
{
namespace detail {
template <typename Tuple, typename F, std::size_t... Indices>
void
tuple_for_each_impl(Tuple &&tuple, F &&f, std::index_sequence<Indices...>)
{
using swallow = int[];
(void)swallow{1, (f(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...};
}
} // ns detail
template <typename Tuple, typename F>
void
for_each(Tuple &&tuple, F &&f)
{
constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
detail::tuple_for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f), std::make_index_sequence<N>{});
}
} // ns functional
struct tuple_tag {};
struct iterator_tag {};
template<typename U, typename TAG>
struct burrito
{
U value;
using TAG_TYPE = TAG;
template<typename T>
burrito(T const& t) : value(t) {}
template<typename T>
burrito(T t0, T t1) : value(std::make_pair(t0, t1)) {}
template<typename FN>
void iterate(FN const fn) const
{
bool constexpr IS_TUPLE = std::is_same<tuple_tag, TAG_TYPE>();
if constexpr (IS_TUPLE) {
functional::for_each(this->value, fn);
} else {
for (auto it{value.first}; it < value.second; ++it) {
fn(*it);
}
}
}
};
template<typename M, typename FN>
void testfn(M const& m, FN const& fn)
{
m.iterate(fn);
}
template<typename T>
auto
make_burrito(T t0, T t1)
{
auto const p = std::make_pair(t0, t1);
return burrito<decltype(p), iterator_tag>{p};
}
template<typename C>
auto
make_burrito(C const& c)
{
return make_burrito(c.cbegin(), c.cend());
}
template<typename ...T>
auto
make_burrito(std::tuple<T...> const& t)
{
using U = std::tuple<T...>;
return burrito<U, tuple_tag>{t};
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// demo
int main()
{
std::tuple<int, int> const tup0 = std::make_tuple(-3, -4);
std::array<int, 10> const arr0 = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
std::vector<int> const v = {32, 42, 52, 62};
auto const m0 = make_burrito(tup0);
auto const m1 = make_burrito(arr0.begin(), arr0.end());
auto const m2 = make_burrito(v);
auto const fn = [](auto const& i) {
std::cerr << std::to_string(i) << "\n";
};
std::cerr << "printing tuple\n";
testfn(m0, fn);
std::cerr << "|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-" << "\n";
std::cerr << "printing array\n";
testfn(m1, fn);
std::cerr << "|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-" << "\n";
std::cerr << "printing vec\n";
testfn(m2, fn);
std::cerr << "|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-" << "\n";
}
Я хотел бы иметь возможность полностью скрыть создание экземпляра буррито от вызывающей стороны, но все же определить аргументы моей функции как буррито.
Как это:
template<typename B, typename FN>
void something(B const& burrito_like, FN const& fn) {
burrito_like.iterate(fn);
}
С звонящим делаем:
auto const tup = ...; // somehow caller creates a tuple
something(tup, []() {});
Это не работает, хотя (очевидно), и компилятор жалуется, что кортеж не реализует итерацию (FN const&);
Я посмотрел на конструкторы неявного преобразования, но не вижу, как бы я указывал различные параметры TAG из неявного конструктора.
Итак, мой вопрос, возможно ли что-то подобное?
Задача ещё не решена.
Других решений пока нет …