使用非成员ostream-overloaded函数打印任何c ++ 11数组

时间:2015-02-20 14:59:25

标签: c++ c++11

C ++ 11中有没有办法让编译器确定函数参数的模板?所以我想打印一个C ++ 11-Array,它只有一个所有类型的stream-operator-function,并且不想为另一个数组类型定义相同的stream-operator-overload:

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

我尝试过这样的事情:

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

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

但编译器不编译这些替代方案。是否有另一个很好的解决方案,或者我真的必须每次为每种类型定义一个重载函数?

3 个答案:

答案 0 :(得分:5)

您需要一个由数组类型参数化的函数模板:

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

模板参数可以从函数参数中推导出来,因此运算符的正常用法很好:

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

如果要支持任意数组

,大小也可以是参数
template <typename T, size_t N>
ostream& operator<<(ostream& stream, const array<T, N>& v)

答案 1 :(得分:1)

在其参数的命名空间之外重载operator<<(或任何其他通过ADL找到的函数)是非常有问题的,并且最终会失败一段时间。由于您无法向std命名空间添加重载,并假设array表示std::array,我建议不要重载操作符。

您可以使用简单的算法打印任何具有迭代器的容器的内容:

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

改进打印输出的样式(更改分隔符而不是在最后一项之后没有它)需要更多时间,但是您应该能够为此编写一个简单的命名函数并调用它来自你现在呼叫operator<<的任何地方。是的,它确实有一些句法开销,是的,你应该这样做。

答案 2 :(得分:1)

直接重载std类型的运算符通常是一个坏主意。

完成这项工作的薄包装器效果很好:

// `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

使用类似于:

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

我们使用函数调用包装我们的array(或其他类似元组),它在流式传输到流时为我们执行漂亮的处理。

作为奖励,上述内容适用于std::tuplestd::array以及std::pair