代码之家  ›  专栏  ›  技术社区  ›  Giogre

C++:类成员函数的模板特化,与另一个成员函数的不同之处仅在于通过引用而不是值接受参数

  •  2
  • Giogre  · 技术社区  · 6 月前

    模板类内 Bank<T> ,我的目标是为类型创建重载 Account 成员职能 void Bank<T>::makeTransfer(T, T, const double) ,签名 void Bank<Account>::makeTransfer(Account&, Account&, const double) .

    也就是说, makeTransfer 当使用调用时,函数应接受引用参数 账户 s、 而它应该复制任何其他类型的参数。

    我所做的,是创造另一个 makeTransfer<T> 具有类似签名的功能,但接受引用 T& 论点而不是 T 然后,我使用以下命令将模板专门化添加到这个新函数中 账户 作为 T .

    然而,下面的MWE没有成功编译,请参阅 GDBonline compiler link .

    #include <cstdio>
    #include <functional>
    #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:
      void makeTransfer(T from, T to, const double amount) {
        printf("ID %ld -> ID %ld: %f\n", from, to, amount);
      }
      
      void makeTransfer(T& from, T& to, const double amount) {}
    };
    
    template<> void Bank<Account>::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() {
      // try with fundamental type as T
      Bank<long> bank;
      bank.makeTransfer(1000L, 2000L, 49.95);
      bank.makeTransfer(2000L, 4000L, 20.00);
    
      // now define a bank with Account instances
      Account a{500, 2600};
      Account b{1000, 10'000};
      Bank<Account> bank2;
    
      bank2.makeTransfer(a, b, 49.95);
      bank2.makeTransfer(b, a, 20.00);
    
      //bank2.makeTransfer(std::ref(a), std::ref(b), 49.95);
      //bank2.makeTransfer(std::ref(b), std::ref(a), 20.00);
    }
    

    编译器抗议无法选择正确的模板实例化:

    main.cpp:58:21: error: call of overloaded ‘makeTransfer(Account&, Account&, double)’ is ambiguous
       58 |   bank2.makeTransfer(a, b, 49.95);
          |   ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
    main.cpp:31:8: note: candidate: ‘void Bank::makeTransfer(T, T, double) [with T = Account]’
       31 |   void makeTransfer(T from, T to, const double amount) {
          |        ^~~~~~~~~~~~
    main.cpp:38:17: note: candidate: ‘void Bank::makeTransfer(T&, T&, double) [with T = Account]’
       38 | template<> void Bank<Account>::makeTransfer(Account& from, Account& to, const double amount) {
          |                 ^~~~~~~~~~~~~
    

    我试图显式地注入引用 std::ref ,但这并没有帮助。

    使用类似的东西 std::same_as<T, Account> 这似乎是多余的,因为编译器的所有提示都应该在模板专门化中可用。

    1 回复  |  直到 6 月前
        1
  •  1
  •   3CxEZiVlQ    6 月前
    1. 从任何银行的角度来看,账户都不能被复制,对吧?因此,必须防止复制:
        Account(const Account&) = delete;
        Account& operator=(const Account&) = delete;
      
    2. 更新银行模板
      #include <type_traits>
      
      template <typename T>
      class Bank {
       public:
        void makeTransfer(T from, T to, const double amount)
          requires(std::is_copy_constructible_v<T> || std::is_copy_assignable_v<T>)
        {
          printf("ID %ld -> ID %ld: %f\n", from, to, amount);
        }
      
        void makeTransfer(T& from, T& to, const double amount) {}
      };
      

    https://godbolt.org/z/9sEaov3K8