I think CDiskBlockIndex::ToString is only used in the fuzz test; and renaming GetBlockHash to CalculateBlockHash would help avoid the "virtual" concerns:
uint256 GetBlockHash() const = delete;
uint256 CalculateBlockHash() const
{
CBlockHeader block;
block.nVersion = nVersion;
...
}
std::string ToString() const = delete;
};
Another alternative might be:
class CDiskBlockIndex
{
private:
CBlockIndex m_cbi;
public:
uint256 hashPrev;
CDiskBlockIndex(const CBlockIndex* pindex)
{
m_cbi.pprev = pindex->pprev;
m_cbi.nHeight = pindex->nHeight;
...
}
void SetCBlockIndex(CBlockIndex* pindex) const
{
pindex->nHeight = m_cbi.nHeight;
...
}
uint256 CalculateBlockHash() const;
SERIALIZE_METHODS(...) { ... }
};
ie, defining the "copy" code explicitly, which would let you move the explicit copy code in CBlockTreeDB::LoadBlockIndexGuts() into diskindex.Set(pindexNew);. At least they're in one place, that way, and you can prevent any implicit copies of CBlockIndex?
I'm not seeing any other ways of improving on this though -- you can't have CDiskBlockIndex use a reference to a CBlockIndex, because the ref has to be non-const for deserialization, and const for serialization, and it can't simultaneously be both; and you can't move de/serialization into CBlockIndex because it needs to store hashPrev on deser, and doesn't have anywhere to do so...