代码之家  ›  专栏  ›  技术社区  ›  smls

奇怪的重复出现的模板模式(CRTP)是正确的解决方案吗?

  •  4
  • smls  · 技术社区  · 6 年前

    脚本

    考虑一个类 Logger write() writeLine() 内部呼叫 :

    class Logger {
      public:
        void write(int x) { ... }
        void write(double x) { ... }
        ...
    
        template <typename T>
        void writeLine(T x) { write(x); ... }
        ...
    };
    

    进一步考虑一个子类 FooLogger 写入() 域特定类型的重载(让我们调用其中的两个 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() 只支持哪个类的参数类型 记录器 写入() 写入() 类中声明的重载 .

    希望

    我用奇怪的重复出现的模板模式(CRTP)让它工作起来:

    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> {
        ...
    };
    

    ( self-contained example program at Ideone

    它在完成任务的同时,也付出了代码复杂性和复杂性增加的代价:

    1. 它使得基类的实现更难阅读(参见Ideone链接),也更难维护(不要忘记 static_cast 以后在课堂中添加更多代码时,请在适当的地方跳舞!)
    2. AbstractLogger 分成两个班。
    3. 因为基类现在是一个类模板,所以它所有成员函数的实现现在必须包含在头中(而不是 .cpp 静态铸造 事情。

    综上所述,我想从C++经验人士那里寻求一些启示:

    • 有没有别的办法解决这个问题?
    2 回复  |  直到 6 年前
        1
  •  5
  •   Jarod42    6 年前

    另一种方法呢:

    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> ...

        2
  •  2
  •   user3738870 YOUSIF ALHURIYA    6 年前

    为什么不使用自由函数,例如。, operator<< ,是在您的类型和记录器的流输出类型上定义的,还是仅在可见时调用的函数?举一个例子来说明如何做到这一点:googletest是这样编写的,这样就可以通过提供序列化方法来定制所有的断言。看到了吗 Teaching Googletest How To Print Your Values

    (注意googletest也有很多方法:可以提供 PrintTo() ,与 如果两者都可用,则首选。这样做的好处是可以序列化为日志记录 不同的 操作员<< 对于你的类,它没有按照你想要的方式处理日志)。

    (魔法都包含在 gtest-printer.h -参见 class UniversalPrinter line 685

    这也是一个优点,它可以很容易地添加任何要正确记录的类/结构/对象,甚至不必费心扩展日志类。此外。。。如果有人扩展logger类(即从它派生)来序列化类,会发生什么情况 AAA ,并且在另一段代码中有一个不同的派生来序列化类 BBB 最后你写了一些代码,你想把两者都记录下来 AAA级 BBB公司 是吗?派生类方法在那里不太好用。。。