[iterator] function_output_iterator constructed from a lambda function is not assignable

Consider the following code snippet: auto f = [](int x) { std::cout << x; };auto it = boost::make_function_output_iterator(f);decltype(it) it2 = it; // Ok, copied it2 = it; // Does not compile, cannot assign! The problem is, function_output_iterator constructed in this way is not assignable, and thus does not satisfy the Iterator <http://en.cppreference.com/w/cpp/concept/Iterator> concept, which requires type to be CopyAssignable <http://en.cppreference.com/w/cpp/concept/CopyAssignable>. This is not a bug, since boost Function Output Iterator documentation <http://www.boost.org/doc/libs/1_60_0/libs/iterator/doc/function_output_iterator.html> clearly says <http://www.boost.org/doc/libs/1_60_0/libs/iterator/doc/function_output_iterator.html#function-output-iterator-requirements> : UnaryFunction must be Assignable and Copy Constructible. While assignment operator of a lambda function <http://en.cppreference.com/w/cpp/language/lambda> is deleted: ClosureType& operator=(const ClosureType&) = delete; So this behaviour is technically correct, but for me is somewhat unexpected. I think it is a perfectly reasonable desire to construct function_output_iterator given a closure produced by lambda function. It seems inconvenient to me why this use case causes a problem. Can you suggest a workaround for this situation? Or maybe a way of improving function_output_iterator to overcome this issue? Personally, I came up with a trick to fix this in a situation, when I'm sure iterator and its copies won't outlive the closure object. In this case we may wrap closure with std::ref: auto it = boost::make_function_output_iterator(std::ref(f)); This works quite fine, but obviously is not always possible. Another options is to store closure in std::function: std::function<void(int)> f = [](int x) { std::cout << x; }; This also works fine and is possible in most contexts, but causes related overheads. See also: http://stackoverflow.com/q/35392919/261217 ----- Best regards, Mikhail Matrosov

Mikhail Matrosov wrote:
Can you suggest a workaround for this situation? Or maybe a way of improving function_output_iterator to overcome this issue?
One way of working around this limitation would be to define a class optional_fn<F> that is like optional<F> but doesn't use F::operator= in its assignment, always destroying the left side and copying instead. Its operator() would forward to F::operator(). So you'd be able to construct a function_output_iterator from make_optional_fn( []... ). This would also make the iterator default constructible although this is not necessary for output iterators. It is necessary for make_transform_iterator though, which would also benefit from a similar treatment. This should arguably be built into the iterators themselves; I think that this has come up before on the list, but I don't remember the outcome of the discussion.

This should arguably be built into the iterators themselves; I think that this has come up before on the list, but I don't remember the outcome of the discussion.
The problem is that there is overhead from using `boost::optional` in all iterators. A better approach is to create fat ranges rather than fat iterators. So instead the function object is stored in the range, and the iterators reference the function from the range. Fat ranges also fixes the problem with dealing with temporary ranges as well. This is the approach that Eric Niebler uses for his ranges-v3, and the reason why he started writing the library: http://ericniebler.com/2013/11/07/input-iterators-vs-input-ranges/ Paul
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Paul Fultz II wrote:
The problem is that there is overhead from using `boost::optional` in all iterators.
What overhead is there? A single bool? This doesn't seem that terrible to me.

On Sunday, February 14, 2016 at 9:20:34 PM UTC-6, Peter Dimov wrote:
Paul Fultz II wrote:
The problem is that there is overhead from using `boost::optional` in all iterators.
What overhead is there? A single bool? This doesn't seem that terrible to me.
I agree, but from what I remember from the discussion others seem to think it was pretty terrible.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Sunday, February 14, 2016 at 6:28:47 PM UTC-6, Mikhail Matrosov wrote:
Consider the following code snippet:
auto f = [](int x) { std::cout << x; };auto it = boost::make_function_output_iterator(f);decltype(it) it2 = it; // Ok, copied it2 = it; // Does not compile, cannot assign!
The problem is, function_output_iterator constructed in this way is not assignable, and thus does not satisfy the Iterator <http://en.cppreference.com/w/cpp/concept/Iterator> concept, which requires type to be CopyAssignable <http://en.cppreference.com/w/cpp/concept/CopyAssignable>.
One way to deal with this is to put it into boost::optional. However, it now needs to be dereferenced. This can be easily done with `fit::indirect`: template<class F> fit::indirect_adaptor<boost::optional<F>> regular(F f) { return fit::indirect(boost::make_optional(f)); } See http://fit.readthedocs.org/en/latest/indirect/index.html Another option is to use `std::ref`, but this requires carefully managing the lifetime of the lambda so it lives as long as the iterator. In some circumstances this is doable. Paul

On Sunday, February 14, 2016 at 9:18:15 PM UTC-6, Peter Dimov wrote:
Paul Fultz II wrote:
One way to deal with this is to put it into boost::optional.
boost::optional<T> uses T::operator= when the source and the target hold a value.
It seems that boost::optional is not constrained properly, the following compiles just fine: struct not_assignable { not_assignable& operator=(const not_assignable&)=delete; }; static_assert(!std::is_copy_assignable<not_assignable>(), "Assignable"); static_assert(std::is_copy_assignable<boost::optional<not_assignable>>(), "Not Assignable"); However, if I use the assignment operator, it breaks with a compile error. Either way `boost::optional` should fallback on in-place new, when there is no assignment operator.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 2016-02-14 22:42, Mikhail Matrosov wrote:
Consider the following code snippet:
auto f = [](int x) { std::cout << x; };auto it = boost::make_function_output_iterator(f);decltype(it) it2 = it; // Ok, copied it2 = it; // Does not compile, cannot assign!
Since the lambda does not capture anything, you might try to convert it to a function pointer, which would make it assignable. typedef void (*lambda_t)(int); auto it = make_function_output_iterator((lambda_t)f); If you do have to capture variables, you might want to use std::bind to store the captures and a pointer to the lambda function without captures. Or just use std::bind with a normal function.

On Monday, February 15, 2016 at 2:36:02 PM UTC-6, Andrey Semashev wrote:
On 2016-02-14 22:42, Mikhail Matrosov wrote:
Consider the following code snippet:
auto f = ;auto it = boost::make_function_output_iterator(f);decltype(it) it2 = it; // Ok, copied it2 = it; // Does not compile, cannot assign!
Since the lambda does not capture anything, you might try to convert it to a function pointer, which would make it assignable.
typedef void (*lambda_t)(int); auto it = make_function_output_iterator((lambda_t)f);
If you do have to capture variables, you might want to use std::bind to store the captures and a pointer to the lambda function without captures. Or just use std::bind with a normal function.
That is another way. Its simpler just to append a `+` to do that though: auto it = make_function_output_iterator(+[](int x) { std::cout << x; }); Paul

Hi, 2016-02-15 4:42 GMT+09:00 Mikhail Matrosov <mikhail.matrosov@gmail.com>:
Consider the following code snippet:
auto f = [](int x) { std::cout << x; };auto it = boost::make_function_output_iterator(f);decltype(it) it2 = it; // Ok, copied it2 = it; // Does not compile, cannot assign!
FYI, My library has `regular` function. The function convert function object to `DefaultConstructible` & `Assignable`. Documentation: http://htmlpreview.github.io/?https://github.com/faithandbrave/OvenToBoost/b... Implementation: https://github.com/Flast/OvenToBoost/blob/master/include/boost/range/regular... https://github.com/Flast/OvenToBoost/blob/master/include/boost/range/detail/... https://github.com/Flast/OvenToBoost/blob/master/include/boost/range/detail/... https://github.com/Flast/OvenToBoost/blob/master/include/boost/range/detail/... The Boost Range Extension library is now waiting review. http://www.boost.org/community/review_schedule.html And FYI, related pull request: https://github.com/boostorg/iterator/pull/20 Thanks, Akira
======================== Akira Takahashi http://faithandbrave.github.io/
participants (5)
-
Akira Takahashi
-
Andrey Semashev
-
Mikhail Matrosov
-
Paul Fultz II
-
Peter Dimov