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;
}
asked Aug 20, 2018 at 14:16
\$\endgroup\$
6
  • \$\begingroup\$ Are those outputs to std::cerr supposed to be in here, or did you forget to remove them before posting? \$\endgroup\$ Commented Aug 20, 2018 at 15:35
  • \$\begingroup\$ just decoration for checking move semantics \$\endgroup\$ Commented Aug 20, 2018 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\$ Commented Aug 20, 2018 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\$ Commented Aug 20, 2018 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\$ Commented Aug 20, 2018 at 15:48

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.