Search code examples
c++boostc++-concepts

Why is boost::function_output_iterator not a std::output_iterator?


I would expect that boost::function_output_iterator is a std::output_iterator, but it surprisingly is not:

#include <boost/iterator/function_output_iterator.hpp>


template<std::output_iterator<int> IntOutIter>
void
f(IntOutIter outputIterator){}


int main(int argc, char const *argv[])
{
  f(boost::make_function_output_iterator([](int i){}));

  return 0;
}

(Note, syntax template<std::output_iterator<int> IntOutIter> from How to declare a template function that takes an output_iterator of T?)

clang++ -std=c++20 function-output-iterator.cpp errors with:

error: no matching function for call to 'f'
note: candidate template ignored: constraints not satisfied [with IntOutIter = function_output_iterator<(lambda at function-output-iterator.cpp:11:42)>]
note: because 'std::output_iterator<boost::iterators::function_output_iterator<(lambda at function-output-iterator.cpp:11:42)>, int>' evaluated to false
note: because 'boost::iterators::function_output_iterator<(lambda at function-output-iterator.cpp:11:42)>' does not satisfy 'input_or_output_iterator'
note: because 'boost::iterators::function_output_iterator<(lambda at function-output-iterator.cpp:11:42)>' does not satisfy 'weakly_incrementable'
note: because 'iter_difference_t<function_output_iterator<(lambda at function-output-iterator.cpp:11:42)>>' (aka 'void') does not satisfy '__is_signed_integer_like'
note: because 'void' does not satisfy 'signed_integral'
note: because 'void' does not satisfy 'integral'
note: because 'is_integral_v<void>' evaluated to false
note: and 'void' does not satisfy '__is_signed_int128'
note: because 'same_as<void, __int128>' evaluated to false
note: because '__detail::__same_as<void, __int128>' evaluated to false
note: because 'std::is_same_v<void, __int128>' evaluated to false
note: and 'same_as<void, __max_diff_type>' evaluated to false
note: because '__detail::__same_as<void, std::ranges::__detail::__max_diff_type>' evaluated to false
note: because 'std::is_same_v<void, std::ranges::__detail::__max_diff_type>' evaluated to false
1 error generated.

Suspected reason

I suspect that this is because boost::function_output_iterator declares

    typedef void                difference_type;

But the standard says it needs to be integral, not void:

  • being an output_iterator requires being a weakly_incrementable (via input_or_output_iterator)
  • this says std::iter_difference_t<T> = ...::difference_type (making the connection between iter_difference_t and difference_type in Boost's code)
  • This comment points out that the standard requires is-signed-integer-like<iter_difference_t<I>> for weakly_incrementable

Common recommendations are to implement difference_type as ptrdiff_t, not void:

Workaround and boost's own claims

I'm aware that this compiles:

-template<std::output_iterator<int> IntOutIter>
+template<IntOutIter>
 void
 f(IntOutIter outputIterator){}

But it removes the benefit of C++ concepts: Contstraining generic type variables for better error messages and indicating what operations they should support.

So, shouldn't function_output_iterator be an output_iterator?

Further, boost docs claim:

function_output_iterator is a model of the Writable and Incrementable Iterator concepts.`

But incrementable requires weakly_incrementable, of which it evidently is not a model!

Question

So, what gives?

Is boost::function_output_iterator implemented wrong?

Or am I not supposed to be able to use it as an output_iterator, and boost docs are wrong?


Solution

  • This is just a guess -- I'm not the author of that particular boost library (well, I'm not the author of ANY boost library...), or associated with boost any more than any other user.

    The date on the boost docs page you linked is from 2006, suggesting that it's rather old and predates C++20 long enough to assume that at the time the library was created, they were still working on C++11.

    Boost generally talks a lot about concepts in their documentation, but they aren't (usually) referring to the C++20 concepts. They use the term to just refer to a table of type-properties, like here. Which is similar to what a C++20 concept is, without the compiler-support.

    Boosts informal concepts and the concepts found in the STL may even have the same names, but that doesn't mean they are 100% the same thing. Maybe the author of that library hasn't found the time to align stuff with the STL. Maybe they have compatibility-concerns concerning existing code. Or maybe they don't do anything with that library anymore.

    We all think that making things STL-compliant is a great idea, but it's not us who are doing the actual work :-)