考虑一个类Logger
,该类具有一个为标准C ++类型重载的成员函数write()
,并且还具有一些方便的函数模板,例如writeLine()
,它们在内部调用write()
:
class Logger {
public:
void write(int x) { ... }
void write(double x) { ... }
...
template <typename T>
void writeLine(T x) { write(x); ... }
...
};
进一步考虑一个子类FooLogger
,该子类为域特定类型添加了额外的write()
重载(我们将其中两个称为FooType1
和FooType2
):
class FooLogger : public Logger {
public:
using Logger::write;
void write(FooType1 x) { ... }
void write(FooType2 x) { ... }
...
};
(self-contained example program at Ideone)
FooLogger::write()
时,支持两个类中的 提供重载的任何参数。
但是,FooLogger::writeLine()
仅支持类Logger
具有write()
重载的参数类型...它看不到在类{中声明的其他write()
重载{1}}。
我想要查看它们,以便也可以使用这些参数类型来调用它!
我使用好奇重复模板模式(CRTP)来工作:
FooLogger
(self-contained example program at Ideone)
虽然可以做到,但代价是增加了代码的复杂性和可读性:
template <typename TDerivedClass>
class AbstractLogger {
...
template <typename T>
void writeLine(T x) { static_cast<TDerivedClass*>(this)->write(x); ... }
};
class Logger : AbstractLogger {}
class FooLogger : public AbstractLogger<FooLogger> {
...
};
舞步。未来!)static_cast
和AbstractLogger
分成两个类。Logger
文件)中,甚至不需要执行这些操作。 .cpp
事。考虑到上述情况,我正在寻求具有C ++经验的人的见解:
答案 0 :(得分:4)
另一种方式:
template <typename ...Ts>
class Logger : private Ts...
{
public:
using Ts::write...;
void write(int x) { /*...*/ }
void write(double x) { /*...*/ }
// ...
template <typename T>
void writeLine(T x) { write(x); /*...*/ }
// ...
};
class FooWriter
{
public:
void write(FooType1 x) { /*...*/ }
void write(FooType2 x) { /*...*/ }
};
using FooLogger = Logger<FooWriter>;
然后使用任何(或它们的别名):
Logger<>
或Logger<FooWriter>
或Logger<FooWriter, BarWriter>
...
答案 1 :(得分:1)
为什么不使用在您的类型和记录器的流输出类型上定义的自由函数,例如operator<<
,或者仅使用可见的调用函数?有关如何执行此操作的示例:编写了googletest,以便您可以通过提供序列化方法来自定义所有断言。请参见Teaching Googletest How To Print Your Values,然后您可以查看实现以查看其操作方式。
(请注意,googletest也有方法:您可以在您的类中提供PrintTo()
方法,也可以重载operator<<
,如果两者均可用,则首选PrintTo()
。这具有优势与序列化为典型输出流相比,您可以序列化为不同地(例如,您的类已经有一个operator<<
了,它并没有做您想要的日志操作)。
(魔法全部包含在gtest-printer.h中-有关触发器,请参见line 685的class UniversalPrinter
。)
这还具有一个优点,即添加任何要正确记录的类/结构/对象非常容易,而无需费心扩展记录类。此外...如果有人扩展记录器类(即从它派生)以序列化类AAA
,会发生什么,而在另一段代码中,有一个不同的派生序列化类BBB
,然后最后,您在要同时记录AAA
和BBB
的地方编写了一些代码?派生类方法在这里效果不佳...