On Tue, Jun 16, 2015 at 11:11 AM, Louis Dionne
Zach Laine
writes: On Tue, Jun 16, 2015 at 9:35 AM, Louis Dionne
wrote:
Zach Laine
writes: [...]
which is closer to your initial implementation. Now, it is obvious that
auto ncolumns = hana::size(head_row);
should work, right?
I also understand why this works.
I do *not* understand why this did not work for me, since it seems to be exactly what the code in your PR does:
static const std::size_t columns = hana::size(std::declval<HeadRow>());
You are using std::declval in an evaluated context, which is illegal. Remember that std::declval is declared (but never defined) as
template <class _Tp> typename add_rvalue_reference<_Tp>::type declval() noexcept; // no definition
I'm using std::declval inside decltype(...), which is an unevaluated context.
Gah! Thanks.
[...]
No problem. Now I understand better. So basically you want to write
tuple_1 foo; auto bar = boost::hana::filter(foo, my_predicate);
I don't understand why that's O(N^2) copies. That should really be N copies, where `N = hana::size(bar)`. As a bonus, if you don't need `foo` around anymore, you can just write
tuple_1 foo; auto bar = boost::hana::filter(std::move(foo), my_predicate);
and now you get N moves, not even N copies.
That's good to know. I was concerned that the pure functional implementation would internally produce intermediate values of size 1, 2, 3, ... N. This is often the case in pure functional implementations. Even so, it returns a temporary that must then be copied/moved again into bar. That means I'm doing 2*N copies/moves instead of N. That implies that I still cannot use filter() in hot code.
The current implementation of filter for Tuple will be as good as I described. The generic implementation for other sequence types (say an adapted std::tuple) will be slower. So there's room for improvement, of course.
When you say "slower" do you mean 2*N or N^2?
(I know that above bar is initialized with the result of filter(), but in many cases, the result will be assigned to an existing value, and the final copy is not guaranteed to be elided. In much of my code, I need that guarantee, or a way to fall back to direct assignment where the elision does not occur.)
The result of `filter` is an rvalue temporary tuple. If the input sequence to filter was a movable-from tuple, it turns out that this rvalue result will have been move-constructed. The rest is up to the thing that receives the result of filter(). If you assign the result of filter() to something that has a move-assignment operator, then no copy occurs. I might be misunderstanding your requirement.
Sometimes extraneous moves are not ok either. I really, really, need to use mutating operations and side effects at least some of the time.
[...]
First, assignment to tuples will be fixed and I consider it a bug right now. However,
[...]
Does that solve your problem, or am I misunderstanding it?
That all works fine, but I actually need assignment across tuple types that are different, but have compatible elements:
hana::_tuple x = ...; hana::_tuple
y = ...; // some stuff happens ...
// This should compile iff std::is_same::value && std::is_same::value x = y;
// But this should work as long as a C is assignable to an A and a D is assignable to a B: hana::copy(x, y);
I guess I will need to decide upon this when I resolve the issue about tuple assignment. It is not yet clear to me why `x = y` should not work when the tuple types are different but have compatible elements. I must think about it.
Well, sometimes C is only explicitly convertible to A. I perhaps overstated things above. What I should have said is, in the general case, "x = y" is not defined for some values, if the assignment relies on implicit conversion. Moreover, I still want to do other mutating operations from one tuple to another, aside from just assignment. Zach