当前位置:  开发笔记 > 编程语言 > 正文

用户定义的文字为C++添加了哪些新功能?

如何解决《用户定义的文字为C++添加了哪些新功能?》经验,为你挑选了8个好方法。

C++ 11引入了用户定义的文字,这将允许基于现有文本(采用新的文本语法的int,hex,string,float),使得任何类型的将能够具有字介绍.

例子:

// imaginary numbers
std::complex operator "" _i(long double d) // cooked form
{ 
    return std::complex(0, d); 
}
auto val = 3.14_i; // val = complex(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(或者说8月和9月不得不写出没有0八进制的原因).这可以通过使语法1974-1/6_AD使得运算符评估顺序起作用但是它很笨重来解决.

那么我的问题归结为这个,你觉得这个功能是否合理?您想要定义哪些其他文字将使您的C++代码更具可读性?


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



1> paercebal..:

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

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

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

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

我们在C++(和C中,我猜),编译器定义的文字中使用,将整数作为短整数或长整数,实数作为float或double(或甚至long double),字符串作为普通或宽字符.

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

我们仍然错过了一件事,让用户类型表现为内置类型:用户定义的文字.

所以,我猜这是语言的自然演变,但要尽可能完整:" 如果你想创建一个类型,并且你希望它像内置类型那样行事,那么这里就是工具. .. "

我猜这与.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 z1(1, 2) ;

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

现在,使用运算符重载,C99"双复数"类型和C++"std :: complex"类型都能够相乘,相加,相减等.

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

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

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

在C++ 11中,您可以自己完成:

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

它臃肿了吗?不,需要存在,如C和C++复合体如何需要一种表示其文字复杂值的方式所示.

这是错误的设计?不,它被设计为其他所有C++功能,并且具有可扩展性.

它仅用于表示法吗?不,因为它甚至可以为您的代码添加类型安全性.

例如,让我们想象一下面向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枪,还有手术刀,泰瑟枪,以及你在工具箱中可以找到的任何其他工具.你可以滥用手术刀并将自己流血至死.或者您可以构建非常优雅和健壮的代码.

因此,像每个C++功能一样,您真的需要它吗?在C++中使用它之前必须回答的问题.如果你不这样做,它将不会花费你.但如果你真的需要它,至少,语言不会让你失望.

日期的例子?

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

1974/01/06AD
    ^  ^  ^

这是无法避免的,因为/作为运算符,编译器必须解释它.而且,AFAIK,这是一件好事.

为了找到问题的解决方案,我会以其他方式编写文字.例如:

"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

就个人而言,我会选择整数和ISO日期,但这取决于您的需求.这是让用户定义自己的文字名称的重点.


@TGV:`你可以写1 + 2i,但你仍然不能写+ bi,所以绝对没有意义':即使忽略你的'a + bi'例子也是荒谬的,你认为它是"低频"的事实"并不意味着每个人都这样做...从大局看,重点是确保用户定义的对象可以尽可能地被认为是语言的一等公民,就像内置类型一样.所以,如果你能写"1.5f"和"1000UL",为什么不能写"25i"甚至"100101b"?与C和Java相反,用户类型不应被视为C++语言的二等公民.
+1.非常好的解释.我们正在等待这个实施.这对我们来说非常重要.我们致力于MDE(模型驱动工程),并发现这是一个必要条件.我在下面添加一个回复来解释我们的案例.
@Anton:`大多数数据仍然来自IO`:代码中有很多硬编码值.看看所有的布尔值,所有整数,代码中的所有双打,因为写'x = 2*y;`而不是`x = Two*y`更方便,其中`Two`是**强类型**常数.用户定义的文字允许我们在其上放置一个类型,并写入:`x = 2_speed*y;`并让编译器验证计算是否有意义...这一切都与强大的打字有关...也许你不会用它.但我确信,只要我能够在工作中使用支持C++ 11的编译器.

2> emsr..:

这是一个使用用户定义的文字而不是构造函数调用的优点:

#include 
#include 

template
  struct checkbits
  {
    static const bool valid = false;
  };

template
  struct checkbits
  {
    static const bool valid = (High == '0' || High == '1')
                   && checkbits::valid;
  };

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

template
  inline constexpr std::bitset
  operator"" _bits() noexcept
  {
    static_assert(checkbits::valid, "invalid digit in binary string");
    return std::bitset((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(至少没有字符串模板参数).


你可以通过给std :: bitset一个合适的constexpr构造函数来做同样的事情.

3> Nils Pipenbr..:

这对数学代码来说非常好.出于我的想法,我可以看到以下运算符的用法:

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

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); 
}

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



4> coppro..:

UDL是命名空间的(并且可以通过使用声明/指令来导入,但是你不能明确命名文字的名称3.14std::i),这意味着(希望)不会有大量的冲突.

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

我只是难过,我们将不会看到标准(从外观上来看)一对夫妇有用的文字,喜欢sstd::stringi为虚数单位.

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



5> Diego Sevill..:

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

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

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

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

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

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

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

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


我非常非常喜欢**编译器在编译时``部分... :-)

6> masonk..:

Bjarne Stroustrup 在关于类型丰富的接口的第一部分中谈到UDL在这个C++ 11演讲中,大约20分钟.

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

    "普通"类型,即内置基元类型,只能捕获琐碎的类型错误.具有更丰富类型的接口允许类型系统捕获更多种类的错误.

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

    在实际代码中,很少使用单位.人们不使用它们,因为产生运行时计算或内存开销来创建丰富类型的代价太高,并且使用预先存在的C++模板化单元代码是如此丑陋以至于没有人使用它.(根据经验,没有人使用它,即使图书馆已经存在了十年).

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



7> Martin Moene..:

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

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,维度的整数幂.



8> mstrobl..:

嗯......我还没有考虑过这个功能.你的样本经过深思熟虑,肯定很有趣.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++那样富有表现力的语言.

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


你的括号是不平衡的!对不起,我的强迫症也讨厌我.
推荐阅读
mobiledu2402852357
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有