简体   繁体   中英

Discarding the output of a function that needs an output iterator

Suppose there´sa template function in C++ that does some useful work but also outputs a sequence of values via an output iterator. Now suppose that that sequence of values sometimes is interesting, but at others is not useful. Is there a ready-to-use iterator class in the STL that can be instantiated and passed to the function and will ignore any values the function tries to assign to the output iterator? To put in another way, send all data to /dev/null?

The STL does not provide such an iterator. But you could code it yourself (tested that code):

struct null_output_iterator : 
    std::iterator< std::output_iterator_tag,
                   null_output_iterator > {
    /* no-op assignment */
    template<typename T>
    void operator=(T const&) { }

    null_output_iterator & operator++() { 
        return *this; 
    }

    null_output_iterator operator++(int) { 
        return *this;
    }

    null_output_iterator & operator*() { return *this; }
};

It doesn't need any data by using itself as the result of operator* . The result of *it = x; is not used in the output iterator requirements, so we can give it a return type of void .


Edit: Let's go into how this operator* works. The Standard says in 24.1.2/1 about the requirements of an output iterator that in both these cases:

*it = t;
*it++ = t;

That the result of those expressions is not used. That's what makes this work:

null_output_iterator it;
*it; // returns a null_output_iterator& per definition of the `operator*`.
*it = some_value; // returns void per definition of the templated `operator=`. 

Now we don't need to have any data that we return in operator* : We just use the iterator itself. Note that the templated operator= does not overwrite the builtin copy assignment operator. It's still provided.

It isn't hard to write one.

template<typename T>
class NullOutputIterator
{
public:
    NullOutputIterator() {}
    NullOutputIterator& operator++() { return *this; }
    NullOutputIterator& operator++(int) { return *this; }
    T& operator*() { return m; }
    T* operator->() { return &m; }
private:
    T m;
};

I haven't tested this, and there's probably something important missing, but I think this is the idea.

Do you have Boost available? If so you could use a function_output_iterator wrapping an empty function.

It's not ideal though. Whatever iterator you use will still need to create an instance of the value_type for return in operator*, even if it then throws it away.

I based mine on std::back_insert_iterator , but without the container:

#include <iterator>

template<typename T>
class NullOutputIter
    : public std::iterator<std::output_iterator_tag,void,void,void,void>
{
public:
    NullOutputIter &operator=(const T &) { return *this; }
    NullOutputIter &operator*() { return *this; }
    NullOutputIter &operator++() { return *this; }
    NullOutputIter operator++(int) { return *this; }
};

This is similar to Johannes's answer, but without the template operator= that takes whatever. I like strong typing; I want *it = wrong_type_thing to be a compile-time error. Also this uses void for the various template parameters to std::iterator , like the output iterators in the standard library.

This is also similar to Mark's solution, but (a) it properly inherits from std::iterator and (b) it does not have the unneeded internal state variable.

Updated answer (2022)

With C++17,std::iterator is deprecated. If you want to avoid a warning, you have to declare the types in the public interface:

  ///
  /// @brief Allows to discard the output of functions that requires an output iterator
  ///
  template<typename T>
  struct null_output_iterator
  {
    using iterator_category = std::forward_iterator_tag;
    using value_type = T;
    using difference_type = T;
    using pointer = T*;
    using reference = T&;
    ///
    /// @brief No-op assignment
    ///
    void operator=(T const&) {}
    ///
    /// @brief Can be pre-incremented
    ///
    null_output_iterator & operator++()
    {
      return *this;
    }
    ///
    /// @brief Can be post-incremented
    ///
    null_output_iterator operator++(int)
    {
      return *this;
    }
    ///
    /// @brief Can be dereferenced
    ///
    null_output_iterator & operator*()
    {
      return *this;
    }
  };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM