如何用boost::units结合scaled_unit与si::unit计算体积?
问题描述
我需要通过面积×高度计算得到单位为mm³的体积,但boost::units没有内置mm²单位,于是尝试用make_scaled_unit自定义单位。参照资料实现后,在boost的quantity.hpp中触发错误:
C2338 (is_implicitly_convertible<Unit2,unit_type>::value == true).
同时伴随包含复杂类型名称的模板实例化提示,错误指向contentVolume赋值语句,代码如下:
#include <iostream> #include <boost/units/systems/si/volume.hpp> #include <boost/units/systems/si/mass.hpp> #include "string" #include <boost/units/systems/si/io.hpp> #include <boost/units/systems/si/prefixes.hpp> #include <boost/units/systems/angle/degrees.hpp> #include <cmath> using namespace boost::units; using namespace boost::units::si; using Volume = boost::units::quantity<boost::units::si::volume>; using Square = boost::units::quantity<boost::units::area_dimension>; // like in stackoverflow answer namespace extended_area_names { namespace squared_millimeters_system { // "area" is not a base unit typedef make_scaled_unit<area, scale<10, static_rational<-6>>>::type millimeter_unit; typedef make_system<millimeter_unit>::type system; typedef unit<area_dimension, system> area; BOOST_UNITS_STATIC_CONSTANT(squared_millimeter, area); BOOST_UNITS_STATIC_CONSTANT(squared_millimeters, area); } // namespace squared_millimeters_system typedef quantity<squared_millimeters_system::area> quantity_area_square_millimeter; using squared_millimeters_system::squared_millimeter; using squared_millimeters_system::squared_millimeters; } // namespace extended_area_names // like in boost documentation, I use only one of these per try typedef make_scaled_unit<area, scale<10, static_rational<-6>>>::type millimeter_unit; Volume ContentVolume(std::string tankType, double contentLevel_raw, double tankHeight_raw, double tankDiameter_raw) { quantity<length> contentLevel(contentLevel_raw * milli * meters); quantity<length> tankHeight(tankHeight_raw * milli * meters); quantity<length> tankDiameter(tankDiameter_raw * milli * meters); quantity<length> tankRadius = tankDiameter / 2.0; Volume tankVolume(3.1415 * (tankRadius * tankRadius) * tankHeight); Volume contentVolume; using namespace extended_area_names; quantity <unit<plane_angle_dimension, degree::system> > sectorAngle(2 * acos((tankRadius - tankHeight) / tankRadius) * degree::degree); quantity<millimeter_unit> sectorSquare( (sectorAngle.value() * (tankRadius.value() * tankRadius.value()) / 2.0) * square_meter); quantity<length> triangleBase( sqrt( (tankRadius.value() * tankRadius.value()) - ((tankRadius.value() - tankHeight.value()) * (tankRadius.value() - tankHeight.value()))) * milli * meters); quantity<length> p( (tankRadius + tankRadius + triangleBase) / 2.0); quantity<millimeter_unit> triangleSquare( sqrt( p.value() * (p.value() - tankRadius.value()) * (p.value() - tankRadius.value()) * (p.value() - triangleBase.value())) * square_meter); // the message says the problem is here contentVolume = (sectorSquare - triangleSquare) * tankHeight; return contentVolume; //m^3 } } }
修复方案
核心问题
自定义的独立单位系统与boost内置SI系统不兼容,导致类型无法隐式转换;同时直接操作数值丢失了单位上下文,引发类型不匹配。
具体修复步骤
复用SI系统定义平方毫米
不需要创建新的单位系统,直接基于SI的area单位缩放,保证兼容性:typedef make_scaled_unit<si::area, scale<10, static_rational<-6>>>::type square_millimeter; BOOST_UNITS_STATIC_CONSTANT(square_millimeters, square_millimeter);避免直接提取数值
value()
用boost::units的运算符直接计算,让库自动处理单位转换,不要手动剥离单位。修正角度单位处理
boost的三角函数默认接受弧度,需将角度转换为弧度后再计算。
修正后的完整代码
#include <iostream> #include <boost/units/systems/si/volume.hpp> #include <boost/units/systems/si/mass.hpp> #include <string> #include <boost/units/systems/si/io.hpp> #include <boost/units/systems/si/prefixes.hpp> #include <boost/units/systems/angle/degrees.hpp> #include <boost/units/conversion.hpp> #include <cmath> using namespace boost::units; using namespace boost::units::si; // 基于SI系统定义平方毫米,无需新建单位系统 typedef make_scaled_unit<si::area, scale<10, static_rational<-6>>>::type square_millimeter; BOOST_UNITS_STATIC_CONSTANT(square_millimeters, square_millimeter); using Volume = quantity<si::volume>; using Area = quantity<si::area>; using SquareMillimeterArea = quantity<square_millimeter>; Volume ContentVolume(std::string tankType, double contentLevel_raw, double tankHeight_raw, double tankDiameter_raw) { quantity<length> contentLevel(contentLevel_raw * milli * meters); quantity<length> tankHeight(tankHeight_raw * milli * meters); quantity<length> tankDiameter(tankDiameter_raw * milli * meters); quantity<length> tankRadius = tankDiameter / 2.0; Volume tankVolume(3.1415 * tankRadius * tankRadius * tankHeight); Volume contentVolume; // 计算扇形角度,转换为弧度(boost三角函数默认用弧度) quantity<plane_angle> sector_angle_rad = 2 * acos((tankRadius - tankHeight) / tankRadius); // 直接用单位计算扇形面积,无需手动提取数值 Area sector_area = (sector_angle_rad * tankRadius * tankRadius) / 2.0; // 计算三角形底边长 quantity<length> triangle_base = sqrt(tankRadius*tankRadius - (tankHeight - tankRadius)*(tankHeight - tankRadius)); // 海伦公式计算三角形面积 quantity<length> p = (tankRadius + tankRadius + triangle_base) / 2.0; Area triangle_area = sqrt(p * (p - tankRadius) * (p - tankRadius) * (p - triangle_base)); // 计算内容物体积,自动处理单位转换 contentVolume = (sector_area - triangle_area) * tankHeight; // 如果需要输出mm³格式,可以转换后输出 SquareMillimeterArea sector_area_mm2 = sector_area; SquareMillimeterArea triangle_area_mm2 = triangle_area; quantity<make_scaled_unit<si::volume, scale<10, static_rational<-9>>>::type> content_volume_mm3 = contentVolume; std::cout << "内容物体积(mm³): " << content_volume_mm3 << std::endl; return contentVolume; // 返回m³单位的体积 } int main() { // 测试调用 Volume vol = ContentVolume("cylinder", 500.0, 1000.0, 800.0); std::cout << "内容物体积(m³): " << vol << std::endl; return 0; }
内容的提问来源于stack exchange,提问作者Ted Denissenko
相关产品推荐
相关产品推荐

