首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >用户定义的文字为C++添加了哪些新功能?

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

Stack Overflow用户
提问于 2008-10-26 09:53:30
回答 12查看 37.5K关注 0票数 144

C++11引入了user-defined literals,它允许在现有文字(inthexstringfloat)的基础上引入新的文字语法,以便任何类型都可以有文字表示。

示例:

代码语言:javascript
复制
// 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 (作为普通的int),然后再计算06_AD (更不用说8月和9月,由于八进制原因,必须在没有0的情况下编写)。这可以通过将语法设置为1974-1/6_AD来解决,这样操作符的求值顺序就可以工作,但它很笨拙。

所以我的问题归结起来就是,你觉得这个特性会证明它自己是正确的吗?您还想定义哪些使您的C++代码更具可读性的文字?

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

EN

回答 12

Stack Overflow用户

回答已采纳

发布于 2011-10-27 01:41:56

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

代码语言:javascript
复制
#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");
}

优点是运行时异常被转换为编译时错误。不能将静态断言添加到接受字符串的位集ctor中(至少不能在没有字符串模板参数的情况下)。

票数 72
EN

Stack Overflow用户

发布于 2008-10-26 14:17:31

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

但当我们更深入地观察时,我们发现它不仅仅是语法上的糖,因为扩展了C++用户的选择,以创建用户定义的类型,这些类型的行为与不同的内置类型完全相同。在这方面,这个小“奖励”是对C++的一个非常有趣的C++11补充。

我们在C++中真的需要它吗?

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

我们在C++ (我猜在C中也是如此)中使用了编译器定义的文字,将整数类型化为短整型或长整型,将实数类型化为浮点型或双精度型(甚至是长双精度型),将字符串类型化为普通字符或宽字符。

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

要让用户类型像内置类型一样工作,我们仍然遗漏了一件事:用户定义的文字。

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

我猜这与.NET的决定非常相似,即让每个原语都成为结构,包括布尔值、整数等,并让所有结构都从Object派生。当使用原语时,这一决定本身就使.NET远远超出了Java的能力范围,无论Java将在其规范中添加多少装箱/拆箱hack。

你真的需要在C++中使用它吗?

这个问题是由来回答的。不是Bjarne Stroustrup不是赫布·萨特。而不是C++标准委员会的任何成员。这就是为什么您可以在C++中进行选择,而且他们不会将有用的符号仅限于内置类型。

如果您需要它,那么它是一个受欢迎的补充。如果你不想,那么..。不要用它。它不会让你付出任何代价。

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

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

臃肿和复杂是有区别的(双关语)。

就像Niels在What new capabilities do user-defined literals add to C++?上展示的那样,能够写一个复数是C和C++“最近”增加的两个特性之一:

代码语言:javascript
复制
// 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“类型和C++ "std::complex”类型都可以使用运算符重载进行乘法、加法、减法等操作。

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

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

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

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

代码语言:javascript
复制
Point p = 25_x + 13_y + 3_z ; // 3D point

它是不是很臃肿?没有,这是有必要的,正如C和C++ complex都需要一种方法来表示其文字复数值所示。

是不是设计错了?不是,它的设计与其他所有C++特性一样,并考虑到了可扩展性。

它是否仅用于表示法?没有,因为它甚至可以给你的代码增加类型安全。

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

代码语言:javascript
复制
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++中使用它之前必须回答的问题。如果你不这样做,它不会让你付出任何代价。但如果你真的需要它,至少语言不会让你失望。

日期示例?

在我看来,您的错误在于您混合了运算符:

代码语言:javascript
复制
1974/01/06AD
    ^  ^  ^

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

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

代码语言:javascript
复制
"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日期,但这取决于您的需要。这就是让用户定义自己的文字名称的全部意义。

票数 195
EN

Stack Overflow用户

发布于 2008-10-26 10:16:32

这对于数学代码来说是非常好的。在我的脑海中,我可以看到以下运算符的用法:

deg表示度。这使得绝对角度的写作更加直观。

代码语言:javascript
复制
double operator ""_deg(long double d)
{ 
    // returns radians
    return d*M_PI/180; 
}

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

代码语言:javascript
复制
int operator ""_fix(long double d)
{ 
    // returns d as a 1.15.16 fixed point number
    return (int)(d*65536.0f); 
}

这些看起来像是如何使用它的很好的例子。它们有助于使代码中的常量更具可读性。这也是另一个让代码变得不可读的工具,但是我们已经有太多的工具被滥用了,再多一个也不会有太大的伤害。

票数 37
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/237804

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档