首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SI型安全单元计算

SI型安全单元计算
EN

Code Review用户
提问于 2018-08-20 10:50:53
回答 1查看 97关注 0票数 4

我写了一个小型的类型丰富的MKS单元系统,用于在日常使用中一致和安全地计算物理单位。

我通过巴顿-纳克曼戏法实现了一些操作符的实现,同时通过一个固定在对象构造上的惟一模板参数来定义类型。这可以防止增加不一致的单位等。

代码语言:javascript
复制
#include <string>
#include <sstream>

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:
  constexpr explicit Value() noexcept = default;
  constexpr explicit Value(long double magnitude) noexcept
      : magnitude_{magnitude} {}
  //constexpr auto &magnitude()  noexcept { return magnitude_; }
  constexpr explicit operator long double() const noexcept {
    return
        magnitude_;
  }

  friend bool operator==(Value const &lhs, Value const &rhs) {
    return static_cast<long double>(lhs)==static_cast<long double>(rhs);
  }
  friend bool operator<(Value const &lhs, Value const &rhs) {
    return static_cast<long double>(lhs) < static_cast<long double>(rhs);
  }

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

 private:
  long double mutable magnitude_{0.0};
};

// Some handy alias declarations
using DimensionlessQuantity = Value<>;
using Length = Value<MksUnit<1, 0, 0>>;
using Area = Value<MksUnit<2, 0, 0>>;
using Volume = Value<MksUnit<3, 0, 0>>;
using Mass = Value<MksUnit<0, 1, 0>>;
using Time = Value<MksUnit<0, 0, 1>>;
using Velocity = Value<MksUnit<1, 0, -1>>;
using Acceleration = Value<MksUnit<1, 0, -2>>;
using Frequency = Value<MksUnit<0, 0, -1>>;
using Force = Value<MksUnit<1, 1, -2>>;
using Pressure = Value<MksUnit<-1, 1, -2>>;
using Momentum = Value<MksUnit<1, 1, -1>>;

// A couple of convenient factory functions
constexpr auto operator "" _N(long double magnitude) {
  return Force{magnitude};
}
constexpr auto operator "" _ms2(long double magnitude) {
  return Acceleration{magnitude};
}
constexpr auto operator "" _s(long double magnitude) {
  return Time{magnitude};
}
constexpr auto operator "" _Ns(long double magnitude) {
  return Momentum{magnitude};
}
constexpr auto operator "" _m(long double magnitude) {
  return Length{magnitude};
}
constexpr auto operator "" _ms(long double magnitude) {
  return Velocity{magnitude};
}
constexpr auto operator "" _kg(long double magnitude) {
  return Mass{magnitude};
}
constexpr auto operator "" _1s(long double magnitude) {
  return Frequency{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)};
}

// Scientific constants
auto constexpr speedOfLight = 299792458.0_ms;
auto constexpr gravitationalAccelerationOnEarth = 9.80665_ms2;

void applyMomentumToSpacecraftBody(Momentum const &impulseValue) {};


int main(){
std::cout << "Consistent? " << 10.0_ms - 5.0_m << std::endl;
}

你介意看一看,告诉我你的想法和我能改进的地方吗?

EN

回答 1

Code Review用户

回答已采纳

发布于 2018-08-20 13:02:03

Value issues

  • friend bool operator==(const Value& lhs, const Value& rhs)可以是noexcept。另外,为什么要使用那些static_casts而不是简单地比较lhs.magnitude == rhs.magnitude呢?这就是为什么它首先是一个friend:允许访问非public成员。
  • 类似于operator<
  • operator+=operator-=:两者都可以是noexcept,在这两个static_cast中都可以用访问other.magnitude代替。
  • auto const &operator*(long double scalar) const那个签名让我头疼。乘法应该返回一个新值,而不是修改它的一个操作数!如果我做了c = b * a; (和a != 1),我就不会期待之后的b == c了。因此,让我们删除返回类型的const &部分,并将函数体更改为返回一个新的Value,其调整幅度为: auto操作符*(长双标量) const,{ _magnitude *标量};}
  • 类似于friend auto& operator*(long double scalar, Value const& other):从返回类型中删除引用。
  • long double mutable magnitude_{0.0};:为什么这需要是mutable (除了使“错误的”标量乘法工作)?

通用材料

  • 请将用户定义的文字放入他们自己的命名空间中。这允许用户选择应该应用哪些文字。我已经可以从<chrono>头看到与文字的冲突了!
  • 是否存在long double按值传递的原因。但Value不是吗?毕竟,它们应该是同样大小的。
  • 正如@TobySpeigh在评论中提到的那样,还有其他SI基础单位,如坎德拉或安培。令人惊讶的是那些都不见了。
  • 此外,是否有理由使用公斤而不是克作为基本单位?1000这个系数不应该有那么大的差别。
  • 在整个实现过程中,操作符*=/=%%=都丢失了。
  • 经过一些努力,大多数(如果不是全部的话)库都可以变成constexpr,从而允许更好的优化(或者在编译时对值进行预计算)。
  • 其中一些“值”不仅有一个量值,而且还有一个方向(即它们是一个向量,而不是标量)。目前的系统无法真正处理这些问题。
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/202043

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档