nit: for non-trivial functions, I think it helps to narrow down T instead of having to deduct it from its callsites and usage.
What do you think about adding an IsCoinRef concept, and adding std::span to the function signature?
 0diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
 1index 97f8241089..ab27387cc4 100644
 2--- a/src/consensus/tx_verify.cpp
 3+++ b/src/consensus/tx_verify.cpp
 4@@ -14,6 +14,8 @@
 5 #include <util/check.h>
 6 #include <util/moneystr.h>
 7 
 8+#include <span>
 9+
10 template <typename T>
11 static constexpr const Coin& GetCoin(const T& item)
12 {
13@@ -158,8 +160,8 @@ template unsigned int GetP2SHSigOpCount<std::span<const Coin>>(
14 template unsigned int GetP2SHSigOpCount<std::span<std::reference_wrapper<const Coin>>>(
15     const CTransaction& tx, const std::span<std::reference_wrapper<const Coin>>);
16 
17-template <typename T>
18-int64_t GetTransactionSigOpCost(const CTransaction& tx, const T coins, uint32_t flags)
19+template<IsCoinRef CoinRef>
20+int64_t GetTransactionSigOpCost(const CTransaction& tx, std::span<CoinRef> coins, uint32_t flags)
21 {
22     int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR;
23 
24@@ -172,18 +174,18 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const T coins, uint32_t
25 
26     Assert(coins.size() == tx.vin.size());
27     auto input_it = tx.vin.begin();
28-    for (auto it = coins.begin(); it != coins.end(); ++it, ++input_it) {
29-        const Coin& coin{GetCoin(*it)};
30+    for (const Coin& coin : coins) {
31         assert(!coin.IsSpent());
32         const CTxOut &prevout = coin.out;
33         nSigOps += CountWitnessSigOps(input_it->scriptSig, prevout.scriptPubKey, &input_it->scriptWitness, flags);
34+        ++input_it;
35     }
36     return nSigOps;
37 }
38-template int64_t GetTransactionSigOpCost<std::span<const Coin>>(
39+template int64_t GetTransactionSigOpCost<const Coin>(
40     const CTransaction& tx, std::span<const Coin> coins, uint32_t flags);
41 
42-template int64_t GetTransactionSigOpCost<std::span<std::reference_wrapper<const Coin>>>(
43+template int64_t GetTransactionSigOpCost<std::reference_wrapper<const Coin>>(
44     const CTransaction& tx, const std::span<std::reference_wrapper<const Coin>> coins, uint32_t flags);
45 
46 template <typename T>
47diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
48index 88b2d6164b..b8ab114306 100644
49--- a/src/consensus/tx_verify.h
50+++ b/src/consensus/tx_verify.h
51@@ -48,6 +48,8 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx);
52 template <typename T>
53 unsigned int GetP2SHSigOpCount(const CTransaction& tx, const T coins);
54 
55+template<typename T>
56+concept IsCoinRef = std::convertible_to<T, const Coin&>;
57 /**
58  * Compute total signature operation cost of a transaction.
59  * [@param](/bitcoin-bitcoin/contributor/param/)[in] tx    Transaction for which we are computing the cost
60@@ -55,8 +57,8 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const T coins);
61  * [@param](/bitcoin-bitcoin/contributor/param/)[in] flags Script verification flags
62  * [@return](/bitcoin-bitcoin/contributor/return/) Total signature operation cost of tx
63  */
64-template <typename T>
65-int64_t GetTransactionSigOpCost(const CTransaction& tx, const T coins, uint32_t flags);
66+template<IsCoinRef CoinRef>
67+int64_t GetTransactionSigOpCost(const CTransaction& tx, std::span<CoinRef> coins, uint32_t flags);
68 
69 /**
70  * Check if transaction is final and can be included in a block with the
(the same applies for GetLegacySigOpCount and GetP2SHSigOpCount)
edit: I see this is already (partially) suggested and addressed in #32317 (review), I think adding the IsCoinRef concept could still be nice but less important than specifying the std::span