2
\$\begingroup\$

This is the continuation of the questions raised in this thread

I did include the improvements that were mentioned but still feel like, I do more copies than I need to. Also I am unsure about the number of copies being made when invoking certain operations on lavlues and rvalues respectively. Is this in general a code you guys can approve of, or are there some major flaws, except for the still missing support of various operations that were mentioned in the other thread?

#include <iostream>

template<typename Value>
struct OperatorFacade {
  friend constexpr bool operator!=(Value const &lhs, Value const &rhs)
  noexcept {
    return !(lhs==rhs);
  }
  friend constexpr bool operator>(Value const &lhs, Value const &rhs) noexcept {
    return rhs < lhs;
  }
  friend constexpr bool operator<=(Value const &lhs, Value const &rhs)
  noexcept {
    return !(rhs > lhs);
  }
  friend constexpr bool operator>=(Value const &lhs, Value const &rhs)
  noexcept {
    return !(rhs < lhs);
  }
  friend constexpr auto &operator<<(std::ostream &os, Value const &other)
  noexcept {
    return os << static_cast<long double>(other);
  }
  friend constexpr auto operator-(Value const &lhs,
                                  Value const &rhs) noexcept {
    return Value{lhs} -= rhs;
  }
  friend constexpr auto operator+(Value const &lhs,
                                  Value const &rhs) noexcept {
    return Value{lhs} += rhs;
  }
};

// Type-safety at compile-time
template<int M = 0, int K = 0, int S = 0>
struct MksUnit {
  enum { metre = M, kilogram = K, second = S };
};

template<typename U = MksUnit<>> // default to dimensionless value
class Value final : public OperatorFacade<Value<U>> {
 public:
  Value(Value const &other) : OperatorFacade<Value>(other) {
    std::clog << "Copy ctor" << std::endl;
  }
  Value(Value &&other) noexcept : OperatorFacade<Value>(std::move(other)) {
    std::clog << "Move ctor" << std::endl;
  }
  auto &operator=(Value const &other) {
    std::clog << "copy assign" << std::endl;
    magnitude_ = other.magnitude_;
    return *this;
  }
  auto &operator=(Value &&other) noexcept {
    std::clog << "Move assign " << std::endl;
    magnitude_ = std::move(other.magnitude_);
    return *this;
  }

  constexpr explicit Value() noexcept = default;
  constexpr explicit Value(long double const &magnitude) noexcept
      : magnitude_{magnitude} {}
  //constexpr auto &magnitude()  noexcept { return magnitude_; }
  constexpr explicit operator long double() const noexcept {
    return magnitude_;
  }

  friend constexpr bool operator==(Value const &lhs, Value const &rhs)noexcept {
    return lhs.magnitude_==rhs.magnitude_;
  }
  friend constexpr bool operator<(Value const &lhs, Value const &rhs)noexcept {
    return lhs < rhs;
  }

  constexpr auto &operator+=(Value const &other) noexcept {
    magnitude_ += other.magnitude_;
    return *this;
  }
  constexpr auto &operator-=(Value const &other) noexcept {
    magnitude_ -= other.magnitude_;
    return *this;
  }
  friend constexpr auto operator*(long double const &scalar, Value const &other)
  noexcept {
    return other*scalar;
  }
  constexpr auto operator*(long double const &scalar) const noexcept {
    return Value{magnitude_*scalar};
  }
  constexpr auto &operator*=(long double const &scalar) noexcept {
    magnitude_ *= scalar;
    return *this;
  }

 private:
  long double magnitude_{0.0};
};

// Some handy alias declarations
using Length = Value<MksUnit<1, 0, 0>>;


namespace si {
// A couple of convenient factory functions
constexpr auto operator "" _m(long double magnitude)noexcept {
  return Length{magnitude};
}
}

// Arithmetic operators for consistent type-rich conversions of SI-Units
template<int M1, int K1, int S1, int M2, int K2, int S2>
constexpr auto operator*(Value<MksUnit<M1, K1, S1>> const &lhs,
                         Value<MksUnit<M2, K2, S2>> const &rhs) noexcept {
  return Value<MksUnit<M1 + M2, K1 + K2, S1 + S2>>{
      static_cast<long double>(lhs)*static_cast<long double>(rhs)};
}

template<int M1, int K1, int S1, int M2, int K2, int S2>
constexpr auto operator/(Value<MksUnit<M1, K1, S1>> const &lhs,
                         Value<MksUnit<M2, K2, S2>> const &rhs) noexcept {
  return Value<MksUnit<M1 - M2, K1 - K2, S1 - S2>>{
      static_cast<long double>(lhs)/static_cast<long double>(rhs)};
}



int main(){
  Length l1 {10};
  Length l2 {20};
  l1 + l2;
}
\$\endgroup\$
  • \$\begingroup\$ Are those outputs to std::cerr supposed to be in here, or did you forget to remove them before posting? \$\endgroup\$ – Toby Speight Aug 20 '18 at 15:35
  • \$\begingroup\$ just decoration for checking move semantics \$\endgroup\$ – Carsten D Aug 20 '18 at 15:36
  • \$\begingroup\$ Yes, but do you intend them to be reviewed, as part of your functioning code? If so, I'll mention that you should be using std::clog rather than std::cerr for things that aren't errors. Otherwise, you can edit before you get any reviews (which "locks in" your code). \$\endgroup\$ – Toby Speight Aug 20 '18 at 15:38
  • \$\begingroup\$ I don't see why you're so concerned about moves and copies - the objects consist of just a double, so not expensive to pass by value at all. \$\endgroup\$ – Toby Speight Aug 20 '18 at 15:42
  • \$\begingroup\$ I merely want to see if there are things that you guys would do differently here. I am extremely grateful for any advise concerning the implementation and so on. Longing to learn \$\endgroup\$ – Carsten D Aug 20 '18 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.