简体   繁体   中英

Print any c++11 array with a non-member ostream-overloaded function

Is there a way in C++11 to let the compiler determine the templates of a function parameter? So I want to print a C++11-Array with only one stream-operator-function for all types and do not want to define same stream-operator-overloads just for another array type:

ostream& operator<<(ostream& stream, const array<float, 3>& v)
{
        stream << v[0] << ", " << v[1] << ", " << v[2];
        return stream;
}

I have tried something like this:

ostream& operator<<(ostream& stream, const array<auto, 3>& v)
{
        stream << v[0] << ", " << v[1] << ", " << v[2];
        return stream;
}

or

ostream& operator<<(ostream& stream, const array<typename...>& v)
{
        stream << v[0] << ", " << v[1] << ", " << v[2];
        return stream;
}

but the compiler doesn't compile these alternatives. Is there another nice solution or do I really have to define each time an overloaded function for each type?

You'd need a function template parametrised by the array type:

template <typename T>
ostream& operator<<(ostream& stream, const array<T, 3>& v)

The template parameter can be deduced from the function argument, so normal usage of the operator is fine:

cout << array<float, 3>{{1,2,3}} << endl;

The size can also be a parameter, if you want to support arbitrary arrays

template <typename T, size_t N>
ostream& operator<<(ostream& stream, const array<T, N>& v)

Directly overloading operators in std types is generally a bad idea.

A thin wrapper that does the job, however, works well:

// `tuple_printer` takes a type and a size.  The size is by default
// deduced from the type.
template<class Tuple, size_t size = std::tuple_size< std::decay_t<Tuple> >{}>
struct tuple_printer {
  // not used:
  using indexes = std::make_index_sequence<size>;
  // all but the last index: (C++14, but easy to write in C++11)
  using pre_indexes = std::make_index_sequence<size-1>;
  // store a forwarding reference to the tuple-like data:
  Tuple&& tuple;
  // The operator<<.  Forwards to other methods.
  friend std::ostream& operator<<(std::ostream& stream, tuple_printer&& self) {
    std::move(self).print_to(pre_indexes{}, stream, ',');
    std::move(self).print_to(std::index_sequence<size-1>{}, stream);
    return stream;
  }
  // printing with a separator:
  template<size_t...Is, class X>
  void print_to(std::index_sequence<Is...>,std::ostream& stream, X&& x)&& {
    // indexes trick, plus array trick.  Looks prettier in C++17:
    int _[]={0,((void)(
      stream << std::get<Is>(std::forward<Tuple>(tuple)) << x
    ),0)...};
    (void)_;
  }
  // printing without a separator:
  template<size_t...Is>
  void print_to(std::index_sequence<Is...>,std::ostream& stream)&& {
    int _[]={0,((void)(
      stream << std::get<Is>(std::forward<Tuple>(tuple))
    ),0)...};
    (void)_;
  }
};
// specialization for empty tuples:
template<class Tuple>
struct tuple_printer<Tuple,0> {
  Tuple&& tuple;
  friend std::ostream& operator<<(std::ostream& stream, tuple_printer&& self) {
    return stream;
  }
};
// function that does the type deduction for you:
template<class Tuple>
tuple_printer<Tuple> print_tuple(Tuple&& t){return {std::forward<Tuple>(t)};}

live example

Use looks like:

std::cout << print_tuple(array) << '\n';

where we wrap our array (or other tuple-like) with a function call, which does the pretty-print handling for us when streamed to a stream.

As a bonus, the above works with std::tuple and std::array and std::pair .

Overloading operator<< (or any other function that is meant to be found through ADL) outside of the namespace of its arguments is quite problematic and will end up failing some time. Since you cannot add overloads to the std namespace, and assuming that array means std::array , I would recommend against overloading the operator altogether.

You can print the contents of any container that has iterators with a simple algorithm:

std::copy(c.begin(), c.end(), std::ostream_iterator<T>(std::cout, " "));

Improving the style of the printout (changing the separator and not having it after the last item) takes a bit more time, but you should be able to write a simple named function for that and call it from wherever you are calling operator<< now. Yes, it does have some syntactic overhead, yes you should do it.

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