C++类成员函数模板特化:传值/传引用差异引发重载歧义
问题解决:Bank模板类makeTransfer函数的重载歧义问题
问题原因
你遇到的编译歧义,本质是当T=Account时,调用bank2.makeTransfer(a, b, 49.95)时,两个重载的makeTransfer都能匹配:
- 传值版本
void makeTransfer(T, T, const double):可以将Account&隐式转换为Account(触发拷贝构造) - 传引用版本
void makeTransfer(T&, T&, const double):直接匹配Account&参数
编译器无法判断你想调用哪个版本,因此报错。
解决方案
方案1:使用C++20 Concepts约束重载函数
通过Concepts限定每个重载的适用场景,确保同一T下只有一个重载生效:
#include <cstdio> #include <concepts> class Account { public: Account() = default; Account(const long id, const double balance): _id{id}, _balance{balance} { printf("Account no.%ld start balance is %f\n", _id, _balance); } const long getId() { return _id; } const double getBalance() { return _balance; } void addToBalance(const double sum) { _balance += sum; } private: long _id; double _balance; }; template<typename T> class Bank { public: // 仅当T不是Account时启用传值版本 void makeTransfer(T from, T to, const double amount) requires (!std::same_as<T, Account>) { printf("ID %ld -> ID %ld: %f\n", from, to, amount); } // 仅当T是Account时启用传引用版本 void makeTransfer(T& from, T& to, const double amount) requires std::same_as<T, Account> { printf("ID %ld -> ID %ld: %f\n", from.getId(), to.getId(), amount); from.addToBalance(-amount); to.addToBalance(amount); printf("Account no.%ld balance is now %f\n", from.getId(), from.getBalance()); printf("Account no.%ld balance is now %f\n", to.getId(), to.getBalance()); } }; int main() { // 基础类型测试 Bank<long> bank; bank.makeTransfer(1000L, 2000L, 49.95); bank.makeTransfer(2000L, 4000L, 20.00); // Account类型测试 Account a{500, 2600}; Account b{1000, 10'000}; Bank<Account> bank2; bank2.makeTransfer(a, b, 49.95); bank2.makeTransfer(b, a, 20.00); }
方案2:完全特化Bank类
直接为Account类型特化整个Bank类,避免重载冲突:
#include <cstdio> class Account { public: Account() = default; Account(const long id, const double balance): _id{id}, _balance{balance} { printf("Account no.%ld start balance is %f\n", _id, _balance); } const long getId() { return _id; } const double getBalance() { return _balance; } void addToBalance(const double sum) { _balance += sum; } private: long _id; double _balance; }; // 泛型Bank类,处理基础类型 template<typename T> class Bank { public: void makeTransfer(T from, T to, const double amount) { printf("ID %ld -> ID %ld: %f\n", from, to, amount); } }; // 特化Account版本的Bank类 template<> class Bank<Account> { public: void makeTransfer(Account& from, Account& to, const double amount) { printf("ID %ld -> ID %ld: %f\n", from.getId(), to.getId(), amount); from.addToBalance(-amount); to.addToBalance(amount); printf("Account no.%ld balance is now %f\n", from.getId(), from.getBalance()); printf("Account no.%ld balance is now %f\n", to.getId(), to.getBalance()); } }; int main() { Bank<long> bank; bank.makeTransfer(1000L, 2000L, 49.95); bank.makeTransfer(2000L, 4000L, 20.00); Account a{500, 2600}; Account b{1000, 10'000}; Bank<Account> bank2; bank2.makeTransfer(a, b, 49.95); bank2.makeTransfer(b, a, 20.00); }
方案3:C++11及以上可用的SFINAE方案
如果无法使用C++20,可通过std::enable_if实现重载的条件启用:
#include <cstdio> #include <type_traits> class Account { public: Account() = default; Account(const long id, const double balance): _id{id}, _balance{balance} { printf("Account no.%ld start balance is %f\n", _id, _balance); } const long getId() { return _id; } const double getBalance() { return _balance; } void addToBalance(const double sum) { _balance += sum; } private: long _id; double _balance; }; template<typename T> class Bank { public: // 非Account类型使用传值版本 template<typename U = T> typename std::enable_if<!std::is_same<U, Account>::value>::type makeTransfer(U from, U to, const double amount) { printf("ID %ld -> ID %ld: %f\n", from, to, amount); } // Account类型使用传引用版本 template<typename U = T> typename std::enable_if<std::is_same<U, Account>::value>::type makeTransfer(U& from, U& to, const double amount) { printf("ID %ld -> ID %ld: %f\n", from.getId(), to.getId(), amount); from.addToBalance(-amount); to.addToBalance(amount); printf("Account no.%ld balance is now %f\n", from.getId(), from.getBalance()); printf("Account no.%ld balance is now %f\n", to.getId(), to.getBalance()); } }; int main() { Bank<long> bank; bank.makeTransfer(1000L, 2000L, 49.95); bank.makeTransfer(2000L, 4000L, 20.00); Account a{500, 2600}; Account b{1000, 10'000}; Bank<Account> bank2; bank2.makeTransfer(a, b, 49.95); bank2.makeTransfer(b, a, 20.00); }
内容的提问来源于stack exchange,提问作者Giogre
相关产品推荐
相关产品推荐

