C++11 介绍 用户定义的文字 这将允许基于现有文字引入新的文字语法(int, hex, string, float)以便任何类型都能够有字面表示。

例子:

// imaginary numbers
std::complex<long double> operator "" _i(long double d) // cooked form
{ 
    return std::complex<long double>(0, d); 
}
auto val = 3.14_i; // val = complex<long double>(0, 3.14)

// binary values
int operator "" _B(const char*); // raw form
int answer = 101010_B; // answer = 42

// std::string
std::string operator "" _s(const char* str, size_t /*length*/) 
{ 
    return std::string(str); 
}

auto hi = "hello"_s + " world"; // + works, "hello"_s is a string not a pointer

// units
assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

乍一看这看起来很酷,但当我试图考虑使用后缀时,我想知道它到底有多适用 _AD_BC 创建日期我发现由于操作员订单而出现问题。 1974/01/06_AD 首先会评估 1974/01 (简单地说 ints) 直到后来 06_AD (更不用说八月和九月必须在没有 0 由于八进制的原因)。这可以通过语法来解决 1974-1/6_AD 这样操作符评估顺序就可以工作,但很笨重。

所以我的问题归结为这个,你觉得这个功能会证明自己是合理的吗?您还想定义哪些其他文字来使您的 C++ 代码更具可读性?


更新语法以适应 2011 年 6 月的最终草案

有帮助吗?

解决方案

以下是使用用户定义的文字而不是构造函数调用的优点:

#include <bitset>
#include <iostream>

template<char... Bits>
  struct checkbits
  {
    static const bool valid = false;
  };

template<char High, char... Bits>
  struct checkbits<High, Bits...>
  {
    static const bool valid = (High == '0' || High == '1')
                   && checkbits<Bits...>::valid;
  };

template<char High>
  struct checkbits<High>
  {
    static const bool valid = (High == '0' || High == '1');
  };

template<char... Bits>
  inline constexpr std::bitset<sizeof...(Bits)>
  operator"" _bits() noexcept
  {
    static_assert(checkbits<Bits...>::valid, "invalid digit in binary string");
    return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
  }

int
main()
{
  auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits;
  std::cout << bits << std::endl;
  std::cout << "size = " << bits.size() << std::endl;
  std::cout << "count = " << bits.count() << std::endl;
  std::cout << "value = " << bits.to_ullong() << std::endl;

  //  This triggers the static_assert at compile time.
  auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits;

  //  This throws at run time.
  std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits");
}

优点是运行时异常转换为编译时错误。 您无法将静态断言添加到采用字符串的bitset ctor(至少没有字符串模板参数)。

其他提示

乍一看,它似乎是简单的语法糖。

但是在深入研究时,我们发现它不仅仅是语法糖,因为它扩展了C ++用户的选项来创建用户定义的类型,其行为与不同的内置类型完全相同。在此,小“奖金”是C ++中非常有趣的C ++ 11补充。

我们真的需要用C ++吗?

我在过去几年编写的代码中看到的用处很少,但仅仅因为我没有在C ++中使用它并不意味着它对另一个C ++开发人员没有意义。

我们在C ++中使用(在C中,我猜),编译器定义的文字,用于输入整数作为短整数或长整数,实数表示为float或double(甚至是long double),字符串正常或广泛的角色。

在C ++中,我们有可能创建自己的类型(即类),可能没有开销(内联等)。我们有可能在它们的类型中添加运算符,使它们的行为类似于类似的内置类型,这使得C ++开发人员能够像使用语言本身一样自然地使用矩阵和复数。我们甚至可以添加强制转换操作符(这通常是一个坏主意,但有时候,它只是正确的解决方案)。

我们仍然错过了一件事,让用户类型的行为类似于内置类型:用户定义的文字。

所以,我想这是语言的自然演变,但要尽可能完整:“如果你想创建一个类型,你希望它的行为尽可能像内置一样类型,这里是工具...... &quot;

我猜这与.NET决定使每个原语成为结构(包括布尔值,整数等)非常类似,并且所有结构都来自Object。这个决定使得.NET在使用原语时远远超出Java的范围,无论Java将在规范中添加多少装箱/拆箱黑客。

你真的需要用C ++吗?

此问题仅供回答。不是Bjarne Stroustrup。不是Herb Sutter。不是C ++标准委员会的成员。这就是您可以选择C ++ 的原因,并且它们不会仅仅为内置类型限制有用的表示法。

如果需要它,那么这是一个受欢迎的补充。如果没有,那么......不要使用它。这将花费你一切。

欢迎使用C ++,这是一种可选功能的语言。

臃肿???告诉我你的复合体!!!

臃肿和复杂(双关语)之间存在差异。

如Niels所示用户定义的文字添加到C ++有哪些新功能?,能够编写复数是”最近“添加的两个功能之一。到C和C ++:

// C89:
MyComplex z1 = { 1, 2 } ;

// C99: You'll note I is a macro, which can lead
// to very interesting situations...
double complex z1 = 1 + 2*I;

// C++:
std::complex<double> z1(1, 2) ;

// C++11: You'll note that "i" won't ever bother
// you elsewhere
std::complex<double> z1 = 1 + 2_i ;

现在,C99“双重复合”都是type和C ++“std :: complex”类型可以使用运算符重载进行相乘,相加,相减等。

但在C99中,他们只是添加了另一种类型作为内置类型,以及内置的运算符重载支持。并且他们添加了另一个内置的文字功能。

在C ++中,他们只是使用了语言的现有功能,看到文字特征是语言的自然演变,因此添加了它。

在C中,如果你需要对另一种类型使用相同的符号增强,那么在你游说添加你的量子波函数(或3D点,或你在工作领域中使用的任何基本类型)之前,你运气不好)作为内置类型的C标准成功。

在C ++ 11中,你可以自己做:

Point p = 25_x + 13_y + 3_z ; // 3D point

臃肿吗?没有,需要存在,如C和C ++复合体如何需要表达方式所示

对于数学代码来说非常好。出于我的想法,我可以看到以下运营商的用途:

度为度。这使得写绝对角度更加直观。

double operator ""_deg(long double d)
{ 
    // returns radians
    return d*M_PI/180; 
}

它还可以用于各种定点表示(在DSP和图形领域仍在使用)。

int operator ""_fix(long double d)
{ 
    // returns d as a 1.15.16 fixed point number
    return (int)(d*65536.0f); 
}

这些看起来很好用,如何使用它。它们有助于使代码中的常量更具可读性。这是使代码不可读的另一种工具,但是我们已经有了太多的工具滥用,而且还有一个不会受到太大伤害。

UDL是命名空间的(并且可以使用声明/指令导入,但是你不能显式地命名像 3.14std :: i 这样的文字),这意味着(希望)不会是大量的冲突。

事实上,它们实际上可以被模板化(并且constexpr)意味着你可以用UDL做一些非常强大的东西。 Bigint的作者会非常高兴,因为他们最终可以拥有任意大的常量,在编译时计算(通过constexpr或模板)。

我很遗憾,我们不会在标准中看到一些有用的文字(从外观上看),比如 s std :: string 假想单位的 i

UDL节省的编码时间实际上并不高,但可读性会大大增加,越来越多的计算可以转移到编译时间,以便更快地执行。

让我补充一点上下文。对于我们的工作,非常需要用户定义的文字。我们致力于MDE(模型驱动工程)。我们想用C ++定义模型和元模型。我们实际上实现了从Ecore到C ++的映射( EMF4CPP )。

当能够在C ++中将模型元素定义为类时,问题就出现了。我们正在采用将元模型(Ecore)转换为带参数的模板的方法。模板的参数是类型和类的结构特征。例如,具有两个int属性的类将类似于:

typedef ::ecore::Class< Attribute<int>, Attribute<int> > MyClass;

然而,事实证明,模型或元模型中的每个元素通常都有一个名称。我们想写一下:

typedef ::ecore::Class< "MyClass", Attribute< "x", int>, Attribute<"y", int> > MyClass;

BUT,C ++和C ++ 0x不允许这样做,因为字符串被禁止作为模板的参数。你可以用char写出char这个名字,但这实在是一团糟。通过适当的用户定义文字,我们可以编写类似的东西。假设我们使用“_n”识别模型元素名称(我不使用确切的语法,只是为了提出想法):

typedef ::ecore::Class< MyClass_n, Attribute< x_n, int>, Attribute<y_n, int> > MyClass;

最后,将这些定义作为模板有助于我们设计用于遍历模型元素,模型转换等的算法,这些算法非常有效,因为类型信息,标识,转换等由编译器在编译时确定时间。

Bjarne Stroustrup 在本文中谈论 UDL C++11 讲座, ,在关于类型丰富的界面的第一部分中,大约 20 分钟。

他对 UDL 的基本论证采用三段论的形式:

  1. “琐碎”类型,即内置原始类型,只能捕获琐碎的类型错误。具有更丰富类型的接口允许类型系统捕获更多类型的错误。

  2. 丰富类型代码可以捕获的类型错误类型会对实际代码产生影响。(他举了火星气候轨道飞行器的例子,该飞行器由于一个重要常数的尺寸错误而臭名昭著地失败了)。

  3. 在实际代码中,很少使用单位。人们不使用它们,因为创建丰富类型所需的运行时计算或内存开销成本太高,而且使用预先存在的 C++ 模板化单元代码在名义上非常丑陋,以至于没有人使用它。(根据经验,没有人使用它,尽管这些库已经存在了十年)。

  4. 因此,为了让工程师在实际代码中使用单元,我们需要一种设备,它 (1) 不会产生运行时开销,并且 (2) 在符号上是可以接受的。

支持编译时维度检查是唯一需要的理由。

auto force = 2_N; 
auto dx = 2_m; 
auto energy = force * dx; 

assert(energy == 4_J); 

例如参见 PhysUnits-CT-Cpp11 ,一个小型C ++ 11,C ++ 14标题 - 仅用于编译时维度分析和单位/数量操纵和转换的库。比 Boost.Units 简单,支持单元符号文字,例如m,g,s, metric前缀,如m,k,M,仅取决于标准C ++库,仅SI,维度的整数幂。

嗯......我还没想过这个功能。你的样本经过深思熟虑,肯定很有趣。 C ++现在非常强大,但不幸的是,您阅读的代码片段中使用的语法有时过于复杂。可读性(如果不是全部的话)至少是那么多。这样的功能可以提高可读性。如果我采取你的最后一个例子

assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

......我想知道你今天如何表达这一点。你有一个KG和一个LB类,你可以比较隐式对象:

assert(KG(1.0f) == LB(2.2f));

那也可以。对于具有较长名称或类型的类型,您不希望为sans编写适配器提供如此好的构造函数,它可能是即时隐式对象创建和初始化的一个很好的补充。另一方面,您也可以使用方法创建和初始化对象。

但我同意尼尔斯的数学观点。例如,C和C ++三角函数需要以弧度为单位输入。我认为在学位上,所以像Nils这样的非常短暂的隐式转换非常好。

最终,它会是语法糖,但它会对可读性产生轻微影响。并且写一些表达式也可能更容易(sin(180.0deg)比sin更容易写(deg(180.0))。然后会有人滥用这个概念。但是,语言滥用的人应该使用非常严格的语言,而不是像C ++那样富有表现力的语言。

啊,我的帖子基本上没什么,除了:它会好的,影响不会太大。我们不用担心。 : - )

我从未需要或想要此功能(但这可能是 Blub 效果) 。我的膝盖反射是一种蹩脚的反应,并且很可能会吸引那些认为让操作员+超负荷运行的任何操作都很酷的人。

C ++对所使用的语法通常非常严格 - 除了预处理器之外,您可以使用很多来定义自定义语法/语法。例如。我们可以重载现有的operatos,但我们无法定义新的操作符 - IMO这非常符合C ++的精神。

我不介意更多定制源代码的一些方法 - 但选择的点似乎对我来说非常孤立,这让我最困惑。

即使是预期的使用也可能使得阅读源代码变得更加困难:单个字母可能具有广泛的副作用,这些副作用绝不能从上下文中识别出来。对于u,l和f的对称性,大多数开发人员将选择单个字母。

这也可能将范围变成一个问题,在全局命名空间中使用单个字母可能会被认为是不好的做法,并且假设混合库更容易的工具(命名空间和描述性标识符)可能会破坏它的目的。

我看到与“auto”结合使用的一些优点,也与提升单位,但不足以值得这个提议。

然而,我想知道我们提出了哪些聪明的想法。

我将用户文字用于二进制字符串,如下所示:

 "asd\0\0\0\1"_b

使用 std :: string(str,n)构造函数,以便 \ 0 不会将字符串切成两半。 (该项目使用各种文件格式进行了大量工作。)

当我抛弃 std :: string 以支持 std :: vector 的包装时,这也很有用。

那东西的线路噪音很大。读起来也很可怕。

让我知道,他们是否推荐使用任何类型的示例添加新语法?例如,他们有几个已经使用C ++ 0x的程序吗?

对我来说,这部分:

auto val = 3.14_i

不能证明这一部分:

std::complex<double> operator ""_i(long double d) // cooked form
{ 
    return std::complex(0, d);
}

即使您在其他1000行使用i语法也不行。如果你写作,你也可能会写上10000行其他内容。特别是当你仍然可能在所有地方写作时:

std::complex<double> val = 3.14i

'auto'-keyword可能是合理的,但也许只是。但是我们只考虑C ++,因为在这方面它比C ++ 0x更好。

std::complex<double> val = std::complex(0, 3.14);

就像......那么简单。甚至认为如果你在任何地方使用它,所有std和尖括号都只是跛脚。我不会开始猜测在C ++ 0x中使用什么语法来转换复杂的std :: complex。

complex = std::complex<double>;

这可能是直截了当的,但我不相信它在C ++ 0x中那么简单。

typedef std::complex<double> complex;

complex val = std::complex(0, 3.14);

也许? &gt; :)

无论如何,重点是:编写3.14i而不是std :: complex(0,3.14);除了极少数特殊情况外,总体上不会为你节省很多时间。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top