用户定义的文字添加到C ++中的新function是什么?

C ++ 11引入了用户定义的文字 ,它将允许基于现有文字( inthexstringfloat )引入新的文字语法,以便任何types都能够具有文字表示。

例子:

 // 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创builddate,我发现,由于操作员的顺序,这是有问题的。 1974/01/06_AD将首先评估1974/01/06_AD (简单的int s),然后才是06_AD (8月和9月没有任何八进制原因必须写0 )。 这可以通过语法为1974-1/6_AD来解决,这样操作员评估顺序就可以工作,但是却很笨拙。

所以我的问题归结为这个,你觉得这个function会certificate自己吗? 你还需要定义哪些其他文字,使得你的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"); } 

其优点是运行时exception转换为编译时错误。 您不能将静态断言添加到位串ctor(至less不是没有string模板参数)。

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

但是当看得更深时,我们发现它不仅仅是语法糖,因为它扩展了C ++用户的选项来创build用户定义的types,其行为与完全不同的内置types完全相同。 在这里,这个小小的“红利”是C ++中非常有趣的C ++ 11。

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

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

我们曾经用过C ++(我想是C),编译器定义的文字,用整型数字来表示整型数字,用float或double(或者甚至是long double)来表示实型数字,用普通或宽字符来表示string。

在C ++中,我们有可能创build自己的types (即类),可能没有开销(内联等)。 我们可以添加运算符到它们的types,让它们像类似的内置types那样运行,这使得C ++开发者可以像使用matrix和复数一样自然地使用matrix和复数。 我们甚至可以添加演员操作员(这通常是一个坏主意,但有时候,这只是正确的解决scheme)。

我们仍然错过了一个让用户typesperformance为内置types的方法:用户定义的文字。

所以,我猜这是语言的自然演变,但要尽可能完整:“ 如果你想创build一个types,并且希望它像内置types一样可行,那么下面是工具。 ..

我猜想这和.NET把每一个原始的结构包括布尔,整数等结合在一起,并且所有的结构都是从Object派生出来都是非常相似的。 仅仅这个决定就使得.NET在处理原语时远远超出了Java的范围,而不pipeJava将多less盒装/拆箱黑客join到它的规范中。

你真的需要它在C ++?

这个问题是给回答的。 不是Bjarne Stroustrup。 不是草药萨特。 不是任何C ++标准委员会的成员。 这就是为什么你在C ++中有select的原因,而且它们不会将有用的表示法仅限制为内置types。

如果需要它,那么这是一个受欢迎的补充。 如果不这样做,那么…不要使用它。 这不会花你什么。

欢迎使用C ++,这是function可选的语言。

臃肿??? 让我看看你的情结!

臃肿和复杂(双关语)之间有区别。

如Niels所示, 用户定义的文字添加到C ++有哪些新function? ,能够编写一个复杂的数字是“近期”添加到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“double complex”types和C ++“std :: complex”types都可以相乘,相加,相减等。

但在C99中,他们只是添加了另一种types的内置types,并且内置了运算符重载支持。 他们又添加了另一个内置的文字function。

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

在C语言中,如果你需要对另一种types进行相同的符号增强,那么直到你将量子波函数(或3D点,或者你在工作领域中使用的任何基本types)添加到C标准作为内置types成功。

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

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

它膨胀了吗? ,需求在那里,正如C和C ++复合体如何需要一种表示其文字复数值的方式所显示的那样。

它是错误的devise? ,它的devise与所有其他C ++function一样,考虑到可扩展性。

仅用于符号目的吗? ,因为它甚至可以为您的代码添加types安全性。

例如,让我们想象一个面向CSS的代码:

 css::Font::Size p0 = 12_pt ; // Ok css::Font::Size p1 = 50_percent ; // Ok css::Font::Size p2 = 15_px ; // Ok css::Font::Size p3 = 10_em ; // Ok css::Font::Size p4 = 15 ; // ERROR : Won't compile ! 

然后很容易强制键入值的分配。

是危险的吗?

好问题。 这些函数可以命名空间吗? 如果是,那么累积奖金!

无论如何, 就像所有的事情一样,如果一个工具使用不当,你可能会自杀 。 C是强大的,如果你滥用C枪,你可以把头部掉下来。 C ++有C枪,还有手术刀,taser,还有其他你可以在工具包中find的工具。 你可以误用手术刀并自杀身亡。 或者你可以build立非常优雅和健壮的代码。

所以,就像每个C ++特性一样,你真的需要它吗? 这是你在C ++中使用之前必须回答的问题。 如果你不这样做,这将会花费你一无所有。 但是,如果你确实需要它,至less,这个语言不会让你失望。

date的例子?

你的错误,在我看来,是你混合操作员:

 1974/01/06AD ^ ^ ^ 

这是不能避免的,因为/作为一个操作符,编译器必须解释它。 而且,AFAIK,这是一件好事。

为了find你的问题的解决scheme,我会以其他方式写文字。 例如:

 "1974-01-06"_AD ; // ISO-like notation "06/01/1974"_AD ; // french-date-like notation "jan 06 1974"_AD ; // US-date-like notation 19740106_AD ; // integer-date-like notation 

就个人而言,我会select整数和ISOdate,但这取决于您的需求。 这是让用户定义自己的文字名称的重点。

math代码非常好。 出于我的想法,我可以看到以下操作符的用法:

度数。 这使得写绝对angular度更直观。

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

它也可以用于各种定点表示(在DSP和graphics领域仍然在使用)。

 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'd)意味着你可以做一些非常强大的东西与UDLs。 Bigint作者将会非常高兴,因为他们终于可以在编译时计算出任意大的常量(通过constexpr或模板)。

我只是很伤心,我们不会在标准中看到一些有用的文字(从它的外观来看),比如std::string s和虚数单位的i

UDL保存的编码时间实际上并不高,但可读性将会大大提高,越来越多的计算可以转移到编译时,以加快执行速度。

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

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

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

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

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

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

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

最后,将这些定义作为模板有助于我们devise遍历模型元素,模型转换等的algorithm,因为types信息,标识,转换等是编译器在编译时确定的。

Bjarne Stroustrup在第一部分关于types丰富的界面的C ++ 11讨论中谈到了UDL,大约20分钟。

他对UDLs的基本论点是三段论的forms:

  1. “Trivial”types,即内置的原始types,只能捕捉平凡的types错误。 更丰富types的接口允许types系统捕捉更多types的错误。

  2. 丰富types的代码可以捕获的types错误对真实代码有影响。 (他给出了火星气候轨道器的例子,由于一个重要的常数的尺寸误差,臭名昭着的失败了)。

  3. 在真正的代码中,单位很less被使用。 人们不使用它们,因为产生运行时计算或内存开销来创build丰富的types代价过高,并且使用预先存在的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, 度量前缀如m,k,M,仅依赖于标准C ++库,SI-only,维度的整数幂。

嗯…我还没有想过这个function。 你的样品是经过深思熟虑的,当然有趣。 现在C ++非常强大,但不幸的是,你读的代码中所用的语法有时过于复杂。 可读性即使不是全部,至less也是如此。 而且这样的function将更适合可读性。 如果我拿你的最后一个例子

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

…我想知道你今天如何expression。 你会有一个KG和一个LB类,你会比较隐式的对象:

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

这样做也可以。 对于具有较长名称或types的types,如果您没有希望为编写适配器的sans写这样一个好的构造函数,那么对于即时隐式对象的创build和初始化来说,这可能是一个很好的补充。 另一方面,您也可以使用方法来创build和初始化对象。

但是我同意Nils在math方面的观点。 例如C和C ++三angular函数需要弧度input。 我认为,尽pipe如此,一个非常短的隐式转换,如尼尔斯发布是非常好的。

最终,这将是语法糖,但它会对可读性产生轻微的影响。 写一些expression式也许会比较容易(sin(180.0deg)比sin(deg(180.0))更容易写,然后就会有人滥用这个概念,但是,语言虐待的人应该使用非常严格的语言,而不是像C ++那样expression的东西。

啊,我的post基本上没有什么,除了:没关系,影响不会太大。 我们不用担心 🙂

我从来没有需要或想要这个function(但这可能是Blub效应)。 我的膝盖反应是跛脚的,很可能会吸引那些认为将操作符+超载的任何操作都可以被远程解释为加法操作的人。

C ++通常对所使用的语法非常严格 – 除了预处理程序之外,没有太多可用于定义自定义语法/语法。 例如,我们可以超载现有操作,但是我们不能定义新操作 – IMO与C ++的精神非常协调。

我不介意更多定制的源代码的一些方法 – 但select的点似乎是非常孤立的,这使我最困惑。

即使是预期的使用也可能使得阅读源代码变得更加困难:单个字母可能具有广泛的副作用,而不能从上下文中识别。 与u,l和f对称,大多数开发者会select单个字母。

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

我看到与“汽车”相结合的一些优点,也结合像增援单位这样的单位图书馆,但还不足以值得这样的赞扬。

不过,我想知道我们有什么聪明的想法。

我使用用户字面值这样的二进制string:

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

使用std::string(str, n)构造函数,以便\0不会将string切成两半。 (该项目用各种文件格式做了很多工作。)

这也是有用的,当我抛弃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 

“自动” – 关键字可能是合理的,但也许只是。 但是让我们只用C ++,因为在这方面比C ++ 0x好。

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

就像那么简单 即使认为所有的标准和尖括号都是蹩脚的,如果你到处使用它。 我不开始猜测在C ++ 0x中有什么语法将std :: complex转换为复杂的。

 complex = std::complex<double>; 

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

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

也许? > 🙂

无论如何,重点是:写3.14i而不是std :: complex(0,3.14); 除less数超特殊情况外,并不能节省您的时间。