奇怪的重复模板模式(CRTP)是正确的解决方案吗? 脚本 考虑一个类 Logger-,它具有标准C++类型重载的成员函数()(代码)>,并且也有一些方便的函数模板,如“代码>写”(代码>,内部调用()/代码>: 类记录器{ 公众: 无效写入(int x){…} 无效写入(双x){…} ... 模板 无效写线(tx){write(x);…} ... };

奇怪的重复模板模式(CRTP)是正确的解决方案吗? 脚本 考虑一个类 Logger-,它具有标准C++类型重载的成员函数()(代码)>,并且也有一些方便的函数模板,如“代码>写”(代码>,内部调用()/代码>: 类记录器{ 公众: 无效写入(int x){…} 无效写入(双x){…} ... 模板 无效写线(tx){write(x);…} ... };,c++,overloading,crtp,C++,Overloading,Crtp,进一步考虑一个子类dougoger,它为域特定类型添加了额外的write()重载(我们调用其中两个FooType1和FooType2): class傻瓜记录器:公共记录器{ 公众: 使用Logger::write; 无效写入(FooType1 x){…} 无效写入(FooType2 x){…} ... }; () 问题 直接调用时,现在支持两个类中任何一个提供重载的任何参数 但是,dougoger::writeLine()仅支持类记录器具有write()重载的参数类型。。。它没有看到在类dou

进一步考虑一个子类
dougoger
,它为域特定类型添加了额外的
write()
重载(我们调用其中两个
FooType1
FooType2
):

class傻瓜记录器:公共记录器{
公众:
使用Logger::write;
无效写入(FooType1 x){…}
无效写入(FooType2 x){…}
...
};
()

问题 直接调用时,现在支持两个类中任何一个提供重载的任何参数

但是,
dougoger::writeLine()
仅支持类
记录器
具有
write()
重载的参数类型。。。它没有看到在类
dougoger
中声明的附加
write()
重载

我想让它看到它们,这样就可以用这些参数类型调用它了

当前解决方案 我使用奇怪的循环模板模式(CRTP)使其工作:

模板
类抽象记录器{
...
模板
void writeLine(tx){static_cast(this)->write(x);…}
};
类记录器:AbstractLogger{}
类傻瓜:公共抽象记录器{
...
};
()

虽然它完成了这项工作,但却以增加代码复杂性和烦躁为代价:

  • 它使基类的实现更难阅读(参见Ideone链接),也更难维护(在将来向类中添加更多代码时,千万不要忘记在适当的地方进行
    静态_cast
    舞蹈!)
  • 它需要将
    AbstractLogger
    Logger
    分为两类
  • 因为基类现在是一个类模板,所以它的所有成员函数的实现现在都必须包含在头文件中(而不是
    .cpp
    文件),即使是那些不需要执行
    静态转换的函数
  • 问题: 考虑到以上问题,我想从C++经验人士那里寻求启示:

    • CRTP是这项工作的正确工具吗
    • 还有别的办法解决这个问题吗

    为什么不使用免费函数,例如,
    操作符另一种方法如何:

    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>;
    
    模板
    类记录器:专用Ts。。。
    {
    公众:
    使用Ts::write。。。;
    无效写入(int x){/*…*/}
    空写(双x){/*…*/}
    // ...
    模板
    无效写线(tx){write(x);/*…*/}
    // ...
    };
    班级编剧
    {
    公众:
    无效写入(FooType1 x){/*…*/}
    无效写入(FooType2 x){/*…*/}
    };
    使用傻瓜记录器=记录器;
    
    然后使用以下任一项(或其别名):


    Logger
    Logger
    Logger
    ..

    顺便问一下,此模式是否有名称?请注意,
    PrintTo
    的自由函数也可能导致ADL和ODR的细微错误。看见每种方法都有其优点和缺点。@Jarod42——事实证明,在我刷新了文档之后——对于GoogleTest
    PrintTo
    来说,它不是一个免费函数,而是一个成员函数。但是,是的,作为一个自由函数可能会有问题。我自己会在一个特定的名称空间中使它成为一个免费函数,因为无论何时扩展任何名称空间(除了
    std
    )都没有问题。你会发现这有什么问题吗?@Jarod42-同样在你的链接答案中(很好的一个BTW),我认为他遇到困难的代码不是因为打印是免费的-而是因为违反了ODR-不管怎样都可能发生。顺便说一句,在我们讨论这个问题时,有没有IDE诊断出这一点?(因为IDE通常有一个完整的程序视图,或者即使编译器没有,也至少有一个较宽的程序视图。)我的观点是,对于多个重载的自由函数,ODR冲突很容易发生,因为我们必须保证在不同的实例化点可以看到相同的重载。检测这一点很难:链接器可能会比较不同obj中的“相同”函数,看看它们是否真的相同,但对于内联代码,似乎为时已晚…@Jarod42-我想我的观点是,除了让OP的方法工作之外,他还要求OP提供其他方法,我认为这种方法具有优势-例如,重量较轻(IMO)。而且,我很少在实践中发现ODR问题(在其他人的代码中只有一到两次,尽管确实令人困惑),但是YMMV!