您在问题中演示的方式是最基本的方式,在各种C++书籍中也可以找到。就我个人而言,我可能不喜欢在我的生产代码中,主要是因为:
- 必须为每个类编写
friend operator<< 的样板代码。
- 添加新的类成员时,您可能还需要单独更新方法。
自 C++14 起,我会推荐以下方式:
图书馆
// Add `is_iterable` trait as defined in https://stackoverflow.com/a/53967057/514235
template<typename Derived>
struct ostream
{
static std::function<std::ostream&(std::ostream&, const Derived&)> s_fOstream;
static auto& Output (std::ostream& os, const char value[]) { return os << value; }
static auto& Output (std::ostream& os, const std::string& value) { return os << value; }
template<typename T>
static
std::enable_if_t<is_iterable<T>::value, std::ostream&>
Output (std::ostream& os, const T& collection)
{
os << "{";
for(const auto& value : collection)
os << value << ", ";
return os << "}";
}
template<typename T>
static
std::enable_if_t<not is_iterable<T>::value, std::ostream&>
Output (std::ostream& os, const T& value) { return os << value; }
template<typename T, typename... Args>
static
void Attach (const T& separator, const char names[], const Args&... args)
{
static auto ExecuteOnlyOneTime = s_fOstream =
[&separator, names, args...] (std::ostream& os, const Derived& derived) -> std::ostream&
{
os << "(" << names << ") =" << separator << "(" << separator;
int unused[] = { (Output(os, (derived.*args)) << separator, 0) ... }; (void) unused;
return os << ")";
};
}
friend std::ostream& operator<< (std::ostream& os, const Derived& derived)
{
return s_fOstream(os, derived);
}
};
template<typename Derived>
std::function<std::ostream&(std::ostream&, const Derived&)> ostream<Derived>::s_fOstream;
用法
为您想要operator<< 设施的那些类继承上述类。自动friend 将通过基础ostream 包含在这些类的定义中。所以没有额外的工作。例如
class MyClass : public ostream<MyClass> {...};
最好在它们的构造函数中,你可以Attach()要打印的成员变量。例如
// Use better displaying with `NAMED` macro
// Note that, content of `Attach()` will effectively execute only once per class
MyClass () { MyClass::Attach("\n----\n", &MyClass::x, &MyClass::y); }
示例
根据您分享的内容,
#include"Util_ostream.hpp"
template<typename T>
class Example : public ostream<Example<T>> // .... change 1
{
public:
Example(const T &_first_ele, const T &_second_ele) : first_(_first_ele), second_(_second_ele)
{
Example::Attach(" ", &Example::first_, &Example::second_); // .... change 2
}
private:
T first_;
T second_;
};
这种方法在每次打印变量时都有一个指针访问,而不是直接访问。从性能的角度来看,这种微不足道的间接性绝不应该成为代码中的瓶颈。
出于实际目的,演示稍微复杂一些。
要求
- 此处的目的是提高打印变量的可读性和一致性
- 无论继承如何,每个可打印类都应有其单独的
ostream<T>
- 一个对象应该定义
operator<<或继承ostream<T>才能编译
设施
现在正在形成一个好的库组件。以下是我目前添加的附加设施。
- 使用
ATTACH()宏,我们也可以通过某种方式打印变量;通过修改库代码,始终可以根据需要自定义可变打印
- 如果基类是可打印的,那么我们可以简单地传递一个类型转换的
this;休息会得到照顾
- 现在支持具有
std::begin/end 兼容性的容器,其中包括vector 以及map
出于快速理解的目的,开头显示的代码较短。有兴趣的可以点击上面的demo链接。