替换枚举以最大化编译时间检查的最佳方法

Best way to replace an enum to maximize compilation time checks

本文关键字:检查 最佳 方法 时间 编译 枚举 最大化 替换      更新时间:2023-10-16

我有一个相当旧的应用程序来处理不同类型的货币。目前,货币存储在枚举中,例如:

enum CURRENCY {
EUR,
USD,
CNY
};
double convertMoney(CURRENCY in, CURRENCY out, double money_in) {
...
}

这工作得很好,除了这不是真正的类型安全:我还有其他包含注释的功能,例如WARNING: all inputs should have the same currency.我的目标是尽可能用编译时检查替换这些注释中的大多数。我可以使用C++17和提升。

我想使用这样的std::variant

class EUR {};
class USD {};
class CNY {};
using CURRENCY = std::variant<EUR,USD,CNY>;
template<typename IN, typename OUT>
class Market {
public:
...
double convertMoney(double in) {
return in*rate;
}
private:
void updateRate() {
....
rate = some_value_fetched_at_runtime;
}
double rate;
};
int main() {
Market<EUR, USD> eur_usd;
Market<EUR, CNY> eur_cny;
std::vector<Market<CURRENCY,CURRENCY>> all_markets{eur_usd, eur_cny};
...
//do something 
...
return 0;
}

但是,当然,这是行不通的,因为我正在尝试将不同类型的市场对象推入我的向量。

那么,综上所述,你们认为在现代C++中替换枚举的最佳方法是什么?如果使用std::variant是正确的,那么解决上述问题的最佳方法是什么?

请注意:

  1. 我可以做类似using Markets = std::variant<Market<EUR,USD>,Market<EUR,CNY>,...>的事情,但这在实践中并不可行,因为我有大约 ~100 种类型的市场,这并没有真正提高可维护性。
  2. 我可以创建一个类CURRENCY并拥有CURRENCYEURUSDCNY子类,但这会在运行时使用v-table,这将减少编译时检查的数量。如果有人能证明我相反,我对此持开放态度。
  3. 我所有的市场都是在我的源代码中定义的(我不需要在运行时动态地创建新的市场(,但汇率是动态变化的。
  1. 您可以将每种货币设置为单独的(模板(类,并在它们之间进行显式转换,就像它们在 ::std::chrono 或 ::boost::unit 中所做的那样。

    template< CURRENCY VCurrencyId > class
    t_Sum
    {
    public: using
    t_Value = double;
    private: t_Value m_value{};
    public: 
    t_Sum(void)
    {}
    public: 
    t_Sum(t_Sum const & other)
    :   m_value{other.m_value}
    {}
    public: explicit 
    t_Sum(t_Value const & value)
    :   m_value{value}
    {}
    public: t_Sum &
    operator =(t_Sum const & other)
    {
    m_value = other.m_value;
    return(*this);
    }
    public: t_Value const &
    Get_Value(void)
    {
    return(m_value);
    }
    public: void
    Set_Value(t_Value const & value)
    {
    m_value = value;
    }
    };
    template< CURRENCY VInputCurrencyId, CURRENCY VOutputCurrencyId > t_Sum< VOutputCurrencyId >
    Convert(t_Sum< VInputCurrencyId > in) {
    ...
    }
    using
    t_Eur = t_Sum< EUR >;
    using
    t_Usd = t_Sum< USD >;
    t_Eur euros{};
    t_Usd bucks{euros};
    // compile-time error, conversion required!
    // or you can add converting constructor
    

你可以做这样的事情:

enum class CURRENCY {
EUR = 0,
USD,
CNY,
LAST = CNY     
};
template <CURRENCY cur> class Price;
// Class to handle convertion
class Market
{
public:
template <CURRENCY Out, CURRENCY In>
Price<Out> convertMoney(const Price<In>&);
void updateChange(CURRENCY in, CURRENCY out, double ratio);
private:
double ratios[int(CURRENCY::Last)][int(CURRENCY::Last)];
};
// Class to represent typed currency value
template <CURRENCY cur>
class Price
{
public:
explicit Price(double value) : value(value) {}
// should only be used when we want the value without the type
double getValue() const { return value; }
// operation which restrict same currency as
Price& operator +=(const Price&);
// ...
private:
double value;
};