物理增强。单位用户自定义字面量

7

现在我们很快就可以使用用户定义字面量(UDL)了,例如在GCC 4.7中。我非常期待物理单位库(例如Boost.Units)使用它们来简化文字表达式,如1+3i3m3meter13_meter。是否有人编写了扩展 Boost.Units,以支持使用UDL的这种行为?


1
考虑到目前唯一支持UDL的编译器是GCC,而且仅在不稳定版本中支持,我猜想不行。此外,不以“_”开头的UDL保留给未来标准使用,您无法自己编写它们。 - Nicol Bolas
知道这点真是太好了!我只是想知道 UDL 如何与当前的数字字面量后缀 lfd 冲突。下划线的要求回答了这个问题。 - Nordlöw
2个回答

5

目前还没有人开发出这样的扩展。只有gcc(可能还有IBM?)拥有UDL,所以可能需要一段时间。我希望某种单位能够进入现在开始的tr2,如果发生这种情况,我相信将会提出用于单位的UDL。

这个有效:

//  ./bin/bin/g++ -std=c++0x -o units4 units4.cpp

#include <boost/units/unit.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si.hpp>

using namespace boost::units;
using namespace boost::units::si;

quantity<length, long double>
operator"" _m(long double x)
{ return x * meters; }

quantity<si::time, long double>
operator"" _s(long double x)
{ return x * seconds; }

int
main()
{
  auto l = 66.6_m;
  auto v = 2.5_m / 6.6_s;
  std::cout << "l = " << l << std::endl;
  std::cout << "v = " << v << std::endl;
}

我认为浏览您最喜欢的单元并执行此操作不会太困难。

关于将这些放入库中: 字面运算符是命名空间范围函数。后缀的竞争会变得混乱不堪。如果我是Boost,我会这样做。

namespace literals
{
...
}

Boost用户可以进行以下操作:
using boost::units::literals;

除了你通常使用的所有using声明之外,您还需要使用上述内容。 这样,您就不会被例如std :: tr2 :: units这样的东西覆盖。 同样,如果您自己编写代码也是如此。

1
烦恼 #1:字面运算符只能接受少数几种参数类型。如果有返回浮点数和双精度浮点数以及长双精度浮点数的版本,那就太好了,这样计算就不会一直提升到长双精度浮点数了。ADL 不会仅根据返回类型选择 - 需要使用特定于精度的命名空间和 using?唉。 - emsr
1
烦恼 #2:除了所有的单位之外,至少拥有常见倍数会更好。例如,长度不仅仅是“m”,还应该包括“km”、“cm”、“mm”等;频率不仅仅是“Hz”,还应该包括“kHz”、“MHz”、“GHz”。这可能会变得很繁琐,或者需要一些宏。 - emsr
有一些应用程序的大小是受限制的。矩阵数学可以属于这个类别。另一方面,将文字转换为所需的大小或为不同的返回类型使用不同的后缀(例如f_km表示浮点千米)并不难。最后,文字往往不会支配代码-它们通常很少。只要对像矩阵这样的大对象进行操作不会使中间变量变得难以管理,那么就可以了。我想这些烦恼并不是什么大问题。 - emsr
我承认在一些(很多)情况下,提升到更高的精度至少是可以接受的,甚至可能是有益的。 - emsr
2
我已经为 Boost 单位实现了一组简单的用户定义字面量,包括所有 S.I. 前缀 这里 - user3317
@AndrewCecil,不错,你有没有考虑过处理组合单位表达式的方法(例如1 m/s,1米每秒)?请看我的回答。 - alfC

3

在我看来,使用Boost.Units的字面量并没有太大的好处,因为通过现有的能力仍然可以实现更强大的语法。

在简单的情况下,似乎使用字面量是正确的选择,但很快你会发现它并不是非常强大。 例如,你仍然需要为组合单位定义字面量,比如怎样表示1 m/s(每秒一米)?

当前:

auto v = 1*si::meter/si::second; // yes, it is long

但是有文本字面值吗?
// fake code
using namespace boost::units::literals;
auto v = 1._m_over_s; // or 1._m/si::second; or 1._m/1_s // even worst

现有功能可以实现更好的解决方案。这就是我所做的:

namespace boost{namespace units{namespace si{ //excuse me!
namespace abbreviations{
    static const length             m   = si::meter;
    static const time               s   = si::second;

    // ...
}
}}} // thank you!

using namespace si::abbreviations;
auto v = 1.*m/s;

同样的方法,您可以这样做:auto a = 1. * m / pow<2>(s);或者如果您想要更多的缩写(例如static const area m2 = pow<2>(si::meter);),则可以扩展缩写。

除此之外,您还需要什么?

也许一个综合解决方案可能是正确的方式。

auto v = 1._m/s; // _m is literal, /s is from si::abbreviation combined with operator/

但是这样会产生大量冗余代码,而且收益微乎其微(在数字后将*替换为_)。
我的解决方案的一个缺点是它使用了常见的单字母名称来污染命名空间。但除了添加下划线(在缩写的开头或结尾添加下划线),例如1.*m_/s_,我没有别的方法,但至少我可以构建真实的单位表达式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接