用户定义的文字添加到C ++中的新function是什么?
C ++ 11引入了用户定义的文字 ,它将允许基于现有文字( int
, hex
, string
, float
)引入新的文字语法,以便任何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:
-
“Trivial”types,即内置的原始types,只能捕捉平凡的types错误。 更丰富types的接口允许types系统捕捉更多types的错误。
-
丰富types的代码可以捕获的types错误对真实代码有影响。 (他给出了火星气候轨道器的例子,由于一个重要的常数的尺寸误差,臭名昭着的失败了)。
-
在真正的代码中,单位很less被使用。 人们不使用它们,因为产生运行时计算或内存开销来创build丰富的types代价过高,并且使用预先存在的C ++模板化单元代码是非常丑陋的,没有人使用它。 (经验上,没有人使用它,即使图书馆已经存在了十年)。
-
因此,为了让工程师使用实际代码中的单元,我们需要一个(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数超特殊情况外,并不能节省您的时间。