Reputation: 14987
Filling multi-dimensional objects (arrays, nested standard containers, etc) in C++ has always been annoying to me. Nested loops are usually used. For example, to fill a 3-dimensional object obj
with value v
, you may write something like
for (auto& x : obj) {
for (auto& y : x) {
for (auto& z : y) {
z = v;
}
}
}
Such loops are code noise, being tedious to write and also hamper code reading. I'm thinking about writing a function template to perform the filling. Ideally, with such a function template, you should be able to write something like
fill(obj, v);
Any ideas?
Currently, I have a function template make_multi()
to make multi-dimensional objects. So you can do
// assuming `obj` is a 3x3x3 nested `std::vector`
obj = make_multi<std::vector>(v, 3, 3, 3);
Besides being more code than the ideal case, this solution is a performance nightmare. Ultimately, I have to look for better ways.
Upvotes: 2
Views: 91
Reputation: 62985
Here is the greedy counterpart to @Columbo's excellent answer:
namespace detail {
using std::begin;
using std::end;
template<typename Elem, typename Func>
constexpr void flat_foreach(Elem&& e, Func& f, long) {
f(std::forward<Elem>(e));
}
template<typename Range, typename Func>
constexpr auto flat_foreach(Range&& r, Func& f, int)
-> decltype(begin(r), void(end(r))) {
for (auto&& i : r) {
flat_foreach(std::forward<decltype(i)>(i), f, 0);
}
}
}
template<typename Range, typename Func>
constexpr void flat_foreach(Range&& r, Func f) {
detail::flat_foreach(std::forward<Range>(r), f, 0);
}
The general advantage to this approach is that one can pass in a generic lambda/functor for f
.
Upvotes: 1
Reputation: 60999
You can write a flattened foreach:
namespace detail {
template <typename Range, typename Func>
constexpr auto flat_foreach(Range&& r, Func& f, int)
-> decltype(void(f(std::forward<Range>(r)))) {f(std::forward<Range>(r));}
template <typename Range, typename Func>
constexpr void flat_foreach(Range&& r, Func& f...) {
for (auto&& i : r)
flat_foreach(std::forward<decltype(i)>(i), f, 0);
}
}
template <typename Range, typename Func>
constexpr void flat_foreach(Range&& r, Func f)
{detail::flat_foreach(std::forward<Range>(r), f, 0);}
Demo. This is the non-greedy approach, passing the range off to the given function as shallow as possible.
Upvotes: 6