鉴于以下内容,工作代码。
#include <iostream>
template<class Detail>
class AbstractLogger
{
public:
static void log(const char* str) {
Detail::log_detailled(str);
}
};
class Logger : public AbstractLogger<Logger>
{
public:
static void log_detailled(const char* str) {
std::cerr << str << std::endl;
}
};
int main(void)
{
AbstractLogger<Logger>::log("main function running!");
return 0;
}
现在,我想将AbstractLogger
放入库中,让库用户定义自己的记录器,就像这里的Logger
类一样。这有一个缺点:AbstractLogger<Logger>
无法在库中使用,因为库无法知道Logger
。
注意:
答案 0 :(得分:2)
如果你的意思是你想要一个使用它作为日志记录机制的库而不知道确切的实例化类型,我会建议反对它。
在满足您的其他要求(即没有虚拟功能)的同时执行此操作的唯一方法是,库中需要记录的所有功能/类型都将转换为采用Logger
类型的模板。最终的结果是你的大部分界面都变成了一个模板(尽管你可能会将大量的实现移植到非模板化的代码中,它会让你的生活比你需要的更难,而且它仍会产生更大的二进制代码)
如果您对虚拟功能的关注是性能,那么您应该重新考虑您的方法及其带来的问题。特别是,记录 非常昂贵。大多数日志库通过优化非日志记录案例来解决它(通过在未启用日志级别/组/ ...的情况下避免调用记录器的宏),但仍然保留实际写入的动态分派。与写入控制台或文件的成本相比,动态调度的成本可以忽略不计,甚至与生成将要记录的消息的成本相比(我假设您不仅记录文字字符串)
答案 1 :(得分:1)
通常的方法是针对概念进行编码,同时提供帮助程序,以便用户可以轻松生成满足一个或多个这些概念的类型。例如,boost::iterator_facade
之类的东西是CRTP助手,它使用户更容易编写迭代器。然后,迭代器可以在接受迭代器的任何地方使用 - 例如在std::vector
的范围构造函数中。请注意该特定构造函数如何不知道用户定义的类型。
在您的情况下,AbstractLogger
将是CRTP帮助程序。缺少的部分将是定义例如记录器的概念。因此,请注意需要记录器的所有内容都需要作为模板实现,或者需要一个类型擦除容器来容纳任意记录器。
概念检查(如Boost提供的那些)对于这种编程很方便,因为它们允许用实际代码表示概念。
答案 2 :(得分:0)
模板类不能“放入库中”,因为它们被编译器实例化为模板参数的特化。
您可以将模板实现中使用的与参数无关的内容放入库中。