首先,大局观。我有一个记录器类。我为该类创建了一个简化的接口(interface),并为该接口(interface)创建了一个库。我想使用 pimpl 来隐藏 Logger 类的实现,因此用户不需要 Logger 的 header 。我在使用模板函数时遇到了麻烦...
Logger header 是这样定义的
/* Logger.h */
class Logger
{
public:
virtual ~Logger(){};
public:
template <typename... Args> void log(const char* fmt, const Args&... args)
{
printf(fmt, &args...);
}
};
std::shared_ptr<Logger> create_logger()
{
return std::shared_ptr<Logger>(new Logger());
}
第一版
我创建了这样的界面
/* LoggerInterface.h */
#include "Logger.h"
class LoggerInterface
{
public:
LoggerInterface();
public:
template <typename... Args> void log(const char* fmt, Args&&... args)
{
logger->log(fmt, std::forward<Args>(args)...);
}
private:
std::shared_ptr<Logger> logger;
};
/* LoggerInterface.cpp */
#include "LoggerInterface.h"
LoggerInterface::LoggerInterface()
{
logger = create_logger();
}
我已经生成了库,这里是使用它的 main.cpp 示例
/* main.cpp */
#include <LoggerInterface.h>
int main()
{
LoggerInterface loggerIntreface;
loggerIntreface.log("Welcome %s\n", "logger");
return 0;
}
一切正常,但主要包含 LoggerInterface.h
,因此隐式包含 Logger.h
。我想在用户代码端摆脱 Logger.h
。
隐藏记录器尝试
我尝试使用 pimpl 习惯用法,但模板函数让我很头疼。我已经阅读了几篇文档并做了很多测试,但到目前为止还没有运气。这是“几乎就在那里”版本的代码。
/* LoggerInterface.h */
class LoggerInterface
{
private:
class LoggerImpl;
public:
LoggerInterface();
public:
template <typename... Args> void log(const char* fmt, Args&&... args);
private:
std::shared_ptr<LoggerImpl> logger;
};
/* LoggerInterfacePrivate.h */
#include "Logger.h"
#include "LoggerInterface.h"
class LoggerImpl : public Logger
{}
template <typename... Args> inline void LoggerInterface::log(const char* fmt, Args&&... args)
{
logger->log(fmt, std::forward<Args>(args)...);
}
/* LoggerInterface.cpp */
#include "LoggerInterfacePrivate.h>
LoggerInterface::LoggerInterface()
{
logger = std::dynamic_pointer_cast<Logger>(create_logger());
}
主要方面没有新内容
/* main.cpp */
#include <LoggerInterface.h>
int main()
{
LoggerInterface loggerIntreface;
loggerIntreface.log("Welcome %s\n", "logger");
return 0;
}
主要包括LoggerInterface.h
,但由于pimpl,不需要Logger.h
。编译正常,但可怕的是我得到了模板 log()
函数的未解析的外部符号错误。
知道如何消除错误吗?我的方法是好的,还是遵循不同的实现来实现我的目标(创建一个用户可以在没有基本 Logger 类 header 的情况下使用的库接口(interface))更好?
重要说明:我无法编辑 Logger 类。
最佳答案
简单的答案是 - 你不能。问题在于接口(interface)方法被实例化为具有每组不同参数的“不同函数”,并且每次都会创建 Logger 函数模板的不同实例化。为此,它需要能够访问 Logger 方法的整个定义,以便能够生成新实例。
如果你隐藏它,你确实会得到所有缺失实例化的未解析外部,因为它们不是在构建 LoggerInterface.cpp 时创建的(因为当时不知道哪些实例化将需要)。
如果只有一些有限的可能实例化集,您可以显式实例化所有需要的版本,并且它们可以从库中导出/通过 PIMPL 使用。然而,由于这里的情况并非如此(log() 方法的参数类型和数量可能是任意的),因此该解决方案在这种情况下不切实际。
关于c++ - 在库中隐藏模板函数声明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45239107/