XRPLF / rippled

Decentralized cryptocurrency blockchain daemon implementing the XRP Ledger protocol in C++
https://xrpl.org
ISC License
4.53k stars 1.47k forks source link

IOUAmount and zero #5170

Open thejohnfreeman opened 3 weeks ago

thejohnfreeman commented 3 weeks ago

IOUAmount has a special representation for zero, but the default constructor IOUAmount() has defaulted implementation, which does not land in that special representation. Some odd behavior I'm observing:

  1. A constant IOUAmount cannot be default constructed without braces.

    IOUAmount const x; // error
    IOUAmount y; // ok
    IOUAmount const z{}; ok

    The error:

    /home/jfreeman/code/rippled/src/test/basics/IOUAmount_test.cpp:48:25: error: uninitialized 'const zzz' [-fpermissive]
    48 |         IOUAmount const zzz;
      |                         ^~~
    In file included from /home/jfreeman/code/rippled/src/test/basics/IOUAmount_test.cpp:20:
    /home/jfreeman/code/rippled/include/xrpl/basics/IOUAmount.h:43:7: note: 'const class ripple::IOUAmount' has no user-provided default constructor
    43 | class IOUAmount : private boost::totally_ordered<IOUAmount>,
      |       ^~~~~~~~~
    /home/jfreeman/code/rippled/include/xrpl/basics/IOUAmount.h:60:5: note: constructor is not user-provided because it is explicitly defaulted in the class body
    60 |     IOUAmount() = default;
      |     ^~~~~~~~~
    /home/jfreeman/code/rippled/include/xrpl/basics/IOUAmount.h:47:18: note: and the implicitly-defined constructor does not initialize 'int64_t ripple::IOUAmount::mantissa_'
    47 |     std::int64_t mantissa_;
      |                  ^~~~~~~~~
  2. A default-constructed IOUAmount compares equal to beast::zero, but not to an IOUAmount constructed with beast::zero. This is because the second comparison calls IOUAmount::operator== (IOUAmount const&), but the first calls operator== (T const&, beast::Zero const&) which defers to calling T::signum() and comparing to 0.

    IOUAmount const z{};
    z == beast::zero; // true
    IOUAmount const zz(beast::zero);
    z == zz; // false

I suspect we need to manually implement the default constructor to fix both of these, but can anyone explain the first behavior?