下面我有两个单独的模板类层次结构(
Account
-> CheckingAccount
和 Logger
-> ConsoleLogger
)和一个使用两者的模板 Bank
类。
目标是使用不同的
Logger
,一种用于 long
等基本类型,另一种针对 Account
对象定制,后者由于 Account
的多态性而采用指针参数。
我认为这可以完全通过模板来实现,但是解决这个问题应该有助于了解模板专业化如何用于类的继承层次结构(如果有的话)。
下面的MWE没有编译成功,参见编译器链接。
#include <cstdio>
//#include <concepts>
//#include <type_traits>
class Account {
public:
virtual ~Account() {}
virtual const long getId() = 0;
};
class CheckingAccount: public Account {
public:
CheckingAccount() = default;
CheckingAccount(const long id): _id{id} {}
~CheckingAccount() {}
const long getId() {
return _id;
}
private:
long _id;
};
////////////////////////////////////////////////////////////////
template<typename T> class Logger {
public:
virtual void logTransfer(T, T, const double) = 0;
};
template<typename T> class ConsoleLogger : public Logger<T> {
public:
void logTransfer(T from, T to, const double amount) override {
printf("[console] %ld -> %ld: %f\n", from, to, amount);
}
};
// template class specialization
template<> class ConsoleLogger<Account*> : public Logger<Account*> {
public:
void logTransfer(Account* from, Account* to, const double amount) override {
printf("[console] %ld -> %ld: %f\n", from->getId(), to->getId(), amount);
}
};
////////////////////////////////////////////////////////////////////
template<typename T> struct Bank {
void setLogger(Logger<T>* new_logger) {
logger = new_logger;
}
void logTransfer(T from, T to, const double amount) {
if(logger)
logger->logTransfer(from, to, amount);
}
private:
Logger<T>* logger;
};
// template class specialization
template<> struct Bank<Account*> {
void setLogger(Logger<Account*>* new_logger) {
logger = new_logger;
}
void logTransfer(Account* from, Account* to, const double amount) {
if(logger)
logger->logTransfer(from, to, amount);
}
private:
Logger<Account*>* logger;
};
/////////////////////////////////////////////
int main() {
// try with long input
ConsoleLogger<long> console_logger;
Bank<long> bank;
bank.setLogger(&console_logger);
bank.logTransfer(500L, 1000L, 23.56);
// try with Account input
CheckingAccount* a = new CheckingAccount{500};
CheckingAccount* b = new CheckingAccount{1000};
printf("Account no.%ld\n", a->getId());
printf("Account no.%ld\n", b->getId());
ConsoleLogger<CheckingAccount*> console_logger2;
Bank<Account*> bank2;
bank2.setLogger(&console_logger2);
bank2.logTransfer(a, b, 42.81);
delete a;
delete b;
}
编译器产量:
main.cpp: In function ‘int main()’:
main.cpp:94:19: error: cannot convert ‘ConsoleLogger*’ to ‘Logger*’
94 | bank2.setLogger(&console_logger2);
| ^~~~~~~~~~~~~~~~
| |
| ConsoleLogger<CheckingAccount*>*
main.cpp:65:36: note: initializing argument 1 of ‘void Bank::setLogger(Logger*)’
65 | void setLogger(Logger<Account*>* new_logger) {
| ~~~~~~~~~~~~~~~~~~^~~~~~~~~~
main.cpp: In instantiation of ‘void ConsoleLogger<T>::logTransfer(T, T, double) [with T = CheckingAccount*]’:
main.cpp:35:8: required from here
main.cpp:36:27: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 2 has type ‘CheckingAccount*’ [-Wformat=]
36 | printf("[console] %ld -> %ld: %f\n", from, to, amount);
| ~~^ ~~~~
| | |
| long int CheckingAccount*
所以简而言之,编译器看不到
ConsoleLogger<Account*>
的模板特化。
我尝试直接从
Logger<Account*>
专门化 Logger<T>
,并从中派生 ConsoleLogger<Account*>
,但从编译器得到了相同的错误消息。
ConsoleLogger<CheckingAccount*>
(您传递给setLogger
的内容)与Logger<Account*>
(setLogger
需要的内容)无关。您可以将 ConsoleLogger<CheckingAccount*>
转换为 Logger<CheckingAccount*>
,但仅此而已。 Logger<CheckingAccount*>
也与 Logger<Account*>
无关。
在您的示例中,您实际上并不需要
ConsoleLogger<CheckingAccount*>
,因为记录器不使用支票帐户特定的内容。所以改变
ConsoleLogger<CheckingAccount*> console_logger2;
到
ConsoleLogger<Account*> console_logger2;
应该可以。