我可以在这里避免模板递归吗?

时间:2016-03-09 14:06:19

标签: c++ recursion range tuples metaprogramming

我为for_each s写了一个tuple

template <typename Tuple, typename F, size_t begin, size_t end>
enable_if_t<begin == end || tuple_size<Tuple>::value < end> for_each(Tuple&, F&&) {
}

template <typename Tuple, typename F, size_t begin = 0U, size_t end = tuple_size<Tuple>::value>
enable_if_t<begin < end && tuple_size<Tuple>::value >= end> for_each(Tuple& t, F&& f) {
    f(get<begin>(t));
    for_each<Tuple, F, begin + 1, end>(t, forward<F>(f));
}

[Live Example]

但是Yakk's answer to this question提供了一个很好的例子,说明如何处理所有tuple非递归地运行lambda

namespace detail {
    template<class F, class...Args>
    void for_each_arg(F&& f, Args&&...args) {
        using detail = int[];

        static_cast<void>(detail{((f(std::forward<Args>(args))), void(), 0)..., 0});
    }
}

template <typename F, typename Tuple>
void for_each_tuple_element(F&& f, Tuple&& t) {
    return experimental::apply([&](auto&&...args) { detail::for_each_arg(forward<F>(f), decltype(args)(args)... ); }, forward<Tuple>(t));   
}

这需要apply。您可以在此处查看Yakk答案的简化:http://ideone.com/yAYjmw

我的问题是这样的:有没有办法以某种方式改进for_each_tuple_element范围,避免我的代码产生的递归?我已经尝试构建{{的子集1}}由范围定义,但如果不使用递归我似乎无法做到这一点,那么为什么不只是我的tuple

4 个答案:

答案 0 :(得分:5)

您可以通过生成with a as ( select a.*, row_number() over (partition by department order by attributeID) rn from attributes a), r as (select level rn from dual connect by level<=3), e as ( select employeeId, department, rn, case when r.rn = 1 then attribute1 when r.rn = 2 then attribute2 when r.rn = 3 then attribute3 end value from employees cross join r ) select e.employeeId, a.attributeid, e.department, a.attribute, a.meaning, e.value from e join a on a.department=e.department and a.rn=e.rn order by e.employeeId, a.attributeid 函数的一系列调用来避免递归,std::get<Is>(t)...索引范围从Isbegin。生成从给定索引开始的连续数字序列相当容易,因为它足以使起始点成为偏移量,然后将其添加到常规索引序列的每个项目中,例如:

end-1

其中序列的长度等于std::get<begin + 0>(t), std::get<begin + 1>(t), ... std::get<begin + n>(t) begin之间的距离。

end

测试:

#include <tuple>
#include <type_traits>
#include <utility>
#include <cstddef>
#include <limits>

template <std::size_t begin, typename Tuple, typename F, std::size_t... Is>
void for_each(Tuple&& t, F&& f, std::index_sequence<Is...>)
{
    using expand = int[];
    static_cast<void>(expand{ 0, (f(std::get<begin + Is>(std::forward<Tuple>(t))), void(), 0)... });
}

template <std::size_t begin = 0U, std::size_t end = std::numeric_limits<std::size_t>::max(), typename Tuple, typename F>
void for_each(Tuple&& t, F&& f)
{
    for_each<begin>(std::forward<Tuple>(t), std::forward<F>(f)
                  , std::make_index_sequence<(end==std::numeric_limits<std::size_t>::max()?std::tuple_size<std::decay_t<Tuple>>::value:end)-begin>{});
}

DEMO

另外,请注意,函数模板的默认模板参数不必放在模板参数列表的末尾,因此,您可以避免丑陋的int main() { auto t = std::make_tuple(3.14, "Hello World!", -1); auto f = [](const auto& i) { std::cout << i << ' '; }; for_each<1>(t, f); for_each<1,3>(t, f); for_each<0,2>(t, f); } 部分。这意味着decltype(t), decltype(f)不能默认为end(因为std::tuple_size<Tuple>::value追溯到Tuple),但此时您只需要一些默认的幻数。

答案 1 :(得分:2)

您可以像这样实现make_index_range元函数:

template <std::size_t Start, std::size_t End>
struct index_range {
    template <std::size_t... Idx>
    static std::index_sequence<(Idx + Start)...>
    make_range (std::index_sequence<Idx...>);

    using type = decltype(make_range(std::make_index_sequence<End-Start>{}));
};

template <std::size_t Start, std::size_t End>
using make_index_range = typename index_range<Start, End>::type;

然后您可以使用它生成std::index_sequence

template <typename Tuple, typename F, std::size_t... Idx>
void for_each(Tuple& t, F&& f, std::index_sequence<Idx...>) {
    (void)std::initializer_list<int> {
        (std::forward<F>(f)(std::get<Idx>(t)), 0)...
    };
}

template <typename Tuple, size_t begin = 0U, 
          size_t end = tuple_size<Tuple>::value, typename F>
enable_if_t<begin < end && tuple_size<Tuple>::value >= end> 
for_each(Tuple& t, F&& f) {
    for_each(t, std::forward<F>(f), make_index_range<begin, end>{});
}

你会像这样使用它:

auto t = std::make_tuple(1, 42.1, "hello world");
for_each<decltype(t), 2, 3>(t,[](auto e){std::cout << e << '\n';});
//outputs hello world

请注意,如果您想要开始和结束,则需要传递decltype(t)。你可以通过使用Peter Skotnicki的答案来避免这种情况。

Live Demo

答案 2 :(得分:2)

受到@Piotr的启发,但有额外的可爱: - )

#include <tuple>
#include <utility>
#include <tuple>
#include <cstddef>
#include <string>
#include <iostream>

template<class Tuple, size_t I>
struct tuple_iterator
{
    constexpr tuple_iterator(Tuple& p) : _p(p) {}

    static constexpr auto index() { return I; }
    constexpr auto operator++() const { return tuple_iterator<Tuple, I+1>(_p); }
    constexpr auto operator--() const { return tuple_iterator<Tuple, I-1>(_p); }
    constexpr decltype(auto) deref() const { return _p; }

    Tuple& _p;
};

template<class...Ts>
constexpr auto begin(const std::tuple<Ts...>& t) {
    return tuple_iterator<const std::tuple<Ts...>, 0>(t);
}

template<class...Ts>
constexpr auto end(const std::tuple<Ts...>& t) {
    return tuple_iterator<const std::tuple<Ts...>, sizeof...(Ts)>(t);
}

template<class Tuple, size_t I>
constexpr auto prev(tuple_iterator<Tuple, I> it) { return --it; }

template<class Tuple, size_t I>
constexpr auto next(tuple_iterator<Tuple, I> it) { return ++it; }

namespace detail
{
    template <
    std::size_t begin,
    typename Tuple,
    typename F,
    std::size_t... Is
    >
    void for_each(Tuple&& t, F&& f, std::index_sequence<Is...>)
    {
        using expand = int[];
        static_cast<void>(expand{ 0, (f(std::get<begin + Is>(std::forward<Tuple>(t))), void(), 0)... });
    }
}

template<class Tuple, size_t First, size_t Last, class Func>
void for_each(tuple_iterator<Tuple, First> first, tuple_iterator<Tuple, Last> last, Func&& f)
{
    constexpr auto dist = Last - First;
    constexpr auto base = First;
    constexpr auto extent = std::make_index_sequence<dist>();
    detail::for_each<base>(first.deref(), std::forward<Func>(f), extent);
}

int main()
{
    using namespace std;

    auto x = make_tuple("dont print me", 1, "two", "three"s, "or me");

    for_each(next(begin(x)), prev(end(x)), [](const auto& x) { cout << x << endl; });

    return 0;
}

预期结果:

1
two
three

答案 3 :(得分:2)

对第二个答案感到抱歉,但我觉得这是值得的。

呈现函数make_poly_tuple_iterator(),它返回迭代器,迭代器将迭代元组中的元素,使用std命名空间中的所有算法。

取消引用迭代器会产生boost :: variant&lt;参考类型...&gt;因此,仿函数必须是boost::static_visitor<>的特化。

下面的演示,使用如下:

int main()
{
    using namespace std;

    auto x = make_tuple(tagged_string<tag1>("dont print me"),
                        1,
                        2.0,
                        tagged_string<tag2>("three"),
                        tagged_string<tag3>("or me"));

    // this is the statically typed version
    for_each(next(begin(x)), 
             prev(end(x)), 
             [](const auto& x) { cout << x << endl; });

    // and the polymorphic version
    auto first = std::next(make_poly_tuple_iterator(begin(x)));
    auto last = std::prev(make_poly_tuple_iterator(end(x)));
    // note: std::for_each ;-)
    std::for_each(first, last, print_it()); 

    return 0;
}

预期结果:

1
2
three
printing: 1
printing: 2
printing: three

完整代码:

是的,我知道,可以做出很多很多改进......

#include <tuple>
#include <utility>
#include <tuple>
#include <cstddef>
#include <string>
#include <iostream>
#include <boost/variant.hpp>
#include <stdexcept>
#include <exception>


template<class Tuple, size_t I>
struct tuple_iterator
{
    constexpr tuple_iterator(Tuple& p) : _p(p) {}

    static constexpr auto index() { return I; }
    static constexpr auto upper_bound() { return std::tuple_size<Tuple>::value; }
    static constexpr auto lower_bound() { return 0; }
    template<size_t I2> static constexpr auto ValidIndex = I2 >= lower_bound() && I2 < upper_bound();
    template<size_t I2, typename = void>
    struct de_ref_type { using type = decltype(std::get<0>(std::declval<Tuple>())); };
    template<size_t I2>
    struct de_ref_type<I2, std::enable_if_t<ValidIndex<I2>>>
    { using type = decltype(std::get<I2>(std::declval<Tuple>())); };

    template<size_t I2> using DerefType = typename de_ref_type<I2>::type;

    constexpr auto operator++() const { return make_like_me<I+1>(); }
    constexpr auto operator--() const { return make_like_me<I-1>(); }

    template<size_t I2, std::enable_if_t<(I2 < lower_bound())>* = nullptr>
    constexpr auto make_like_me() const { return tuple_iterator<Tuple, lower_bound()>(_p); }

    template<size_t I2, std::enable_if_t<(I2 >= upper_bound())>* = nullptr>
    constexpr auto make_like_me() const { return tuple_iterator<Tuple, upper_bound()>(_p); }

    template<size_t I2, std::enable_if_t<ValidIndex<I2>>* = nullptr>
    constexpr auto make_like_me() const { return tuple_iterator<Tuple, I2>(_p); }

    constexpr decltype(auto) deref() const { return _p; }

    template<size_t X> bool operator==(const tuple_iterator<Tuple, X>& r) const { return false; }
    bool operator==(const tuple_iterator<Tuple, I>& r) const {
        return std::addressof(_p) == std::addressof(r._p);
    }

    template<size_t I2, std::enable_if_t<ValidIndex<I2>>* =nullptr>
    DerefType<I2> impl_star() const { return std::get<I2>(_p); }

    template<size_t I2, std::enable_if_t<not ValidIndex<I2>>* =nullptr>
    DerefType<I2> impl_star() const
    { throw std::logic_error("out of range"); }

    decltype(auto) operator*() const {
        return impl_star<index()>();
    }

    Tuple& _p;
};

template<class...Ts>
constexpr auto begin(const std::tuple<Ts...>& t) {
    return tuple_iterator<const std::tuple<Ts...>, 0>(t);
}

template<class...Ts>
constexpr auto end(const std::tuple<Ts...>& t) {
    return tuple_iterator<const std::tuple<Ts...>, sizeof...(Ts)>(t);
}

template<class Tuple, size_t I>
constexpr auto prev(tuple_iterator<Tuple, I> it) { return --it; }

template<class Tuple, size_t I>
constexpr auto next(tuple_iterator<Tuple, I> it) { return ++it; }

namespace detail
{
    template <
    std::size_t begin,
    typename Tuple,
    typename F,
    std::size_t... Is
    >
    void for_each(Tuple&& t, F&& f, std::index_sequence<Is...>)
    {
        using expand = int[];
        static_cast<void>(expand{ 0, (f(std::get<begin + Is>(std::forward<Tuple>(t))), void(), 0)... });
    }
}

template<class Tuple, size_t First, size_t Last, class Func>
void for_each(tuple_iterator<Tuple, First> first, tuple_iterator<Tuple, Last> last, Func&& f)
{
    constexpr auto dist = Last - First;
    constexpr auto base = First;
    constexpr auto extent = std::make_index_sequence<dist>();
    detail::for_each<base>(first.deref(), std::forward<Func>(f), extent);
}

namespace detail {

    template<class Tuple>
    struct variant_of_tuple;

    template<class...Ts>
    struct variant_of_tuple<std::tuple<Ts...>>
    {
        // todo: some work to remove duplicates
        using type = boost::variant<std::add_lvalue_reference_t<Ts>...>;
    };

    template<class...Ts>
    struct variant_of_tuple<const std::tuple<Ts...>>
    {
        // todo: some work to remove duplicates
        using type = boost::variant<std::add_lvalue_reference_t<std::add_const_t<Ts>>...>;
    };
}

template<class Tuple>
using ToVariant = typename detail::variant_of_tuple<Tuple>::type;

template<class Tuple>
struct poly_tuple_iterator
{
    using tuple_type = Tuple;
    using value_type = ToVariant<std::remove_reference_t<tuple_type>>;
    using difference_type = std::ptrdiff_t;
    using pointer = value_type*;
    using reference = value_type&;
    using iterator_category = std::random_access_iterator_tag;


    struct concept {
        virtual ~concept() = default;
        virtual const std::type_info& type() const = 0;
        virtual const void* address() const = 0;
        virtual bool equal(const void* p) const = 0;
        virtual std::unique_ptr<concept> clone() const = 0;
        virtual std::unique_ptr<concept> next() const = 0;
        virtual std::unique_ptr<concept> prev() const = 0;
        virtual value_type deref() const = 0;
    };

    template<size_t I>
    struct model : concept {
        using my_type = tuple_iterator<tuple_type, I>;
        model(my_type iter) : _iter(iter) {}
        const std::type_info& type() const override { return typeid(_iter); }
        const void* address() const override { return std::addressof(_iter); }
        std::unique_ptr<concept> clone() const override { return std::make_unique<model<I>>(_iter); };
        std::unique_ptr<concept> next() const override {
            auto next_iter = ++_iter;
            return std::make_unique<model<next_iter.index()>>(next_iter);
        };
        std::unique_ptr<concept> prev() const override {
            auto next_iter = --_iter;
            return std::make_unique<model<next_iter.index()>>(next_iter);
        };
        value_type deref() const override { return { *_iter }; }
        bool equal(const void* p) const override {
            return _iter == *reinterpret_cast<const my_type*>(p);
        }
        my_type _iter;
    };

    template<size_t I>
    poly_tuple_iterator(tuple_iterator<tuple_type, I> iter)
    : _impl(std::make_unique<model<I>>(iter))
    {}

    poly_tuple_iterator(const poly_tuple_iterator& r) : _impl(r._impl->clone()) {};
    poly_tuple_iterator(poly_tuple_iterator&& r) : _impl(std::move(r._impl)) {};
    poly_tuple_iterator& operator=(const poly_tuple_iterator& r) {
        _impl = r._impl->clone();
        return *this;
    }
    poly_tuple_iterator& operator=(poly_tuple_iterator&& r) {
        auto tmp = r._impl->clone();
        std::swap(tmp, _impl);
        return *this;
    }

    value_type operator*() const { return _impl->deref(); }
    poly_tuple_iterator& operator++() { _impl = _impl->next(); return *this; }
    poly_tuple_iterator operator++(int) { auto tmp = *this; _impl = _impl->next(); return tmp; }
    poly_tuple_iterator& operator--() { _impl = _impl->prev(); return *this; }
    poly_tuple_iterator operator--(int) { auto tmp = *this; _impl = _impl->prev(); return tmp; }
    poly_tuple_iterator& operator+=(difference_type dist) {
        while (dist > 0) {
            ++(*this);
            --dist;
        }
        while(dist < 0) {
            --(*this);
            ++dist;
        }
        return *this;
    }
    bool operator==(const poly_tuple_iterator& r) const {
        return _impl->type() == r._impl->type()
        and _impl->equal(r._impl->address());
    }
    bool operator!=(const poly_tuple_iterator& r) const {
        return not (*this == r);
    }

private:
    std::unique_ptr<concept> _impl;
};

template<class Tuple, size_t I>
auto make_poly_tuple_iterator(tuple_iterator<Tuple, I> iter) {
    return poly_tuple_iterator<Tuple>(iter);
}

struct print_it : boost::static_visitor<void>
{

    template<class T>
    void operator()(const T& t) const {
        std::cout << "printing: " << t << std::endl;
    }

    template<class...Ts>
    void operator()(const boost::variant<Ts...>& v) const {
        boost::apply_visitor(*this, v);
    }
};

// to differentiate string types for this demo
template<class tag>
struct tagged_string : std::string
{
    using std::string::string;
};

struct tag1 {};
struct tag2 {};
struct tag3 {};

int main()
{
    using namespace std;

    auto x = make_tuple(tagged_string<tag1>("dont print me"),
                        1,
                        2.0,
                        tagged_string<tag2>("three"),
                        tagged_string<tag3>("or me"));

    for_each(next(begin(x)), prev(end(x)), [](const auto& x) { cout << x << endl; });

    auto first = std::next(make_poly_tuple_iterator(begin(x)));
    auto last = std::prev(make_poly_tuple_iterator(end(x)));
    std::for_each(first, last, print_it());

    return 0;
}