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

发布于 2024-07-08 02:44:21 字数 1375 浏览 3 评论 0原文

C++11 引入用户定义的文字 这将允许基于现有文字引入新的文字语法(< code>int、hexstringfloat),以便任何类型都能够进行文字表示。

示例:

// 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< 时,我想知道它到底有多适用/code> 创建日期 我发现由于操作员顺序而出现问题。 1974/01/06_AD 将首先评估 1974/01(作为普通 int),然后才评估 06_AD (更不用说八月和九月由于八进制原因而必须不写0)。 可以通过将语法设置为 1974-1/6_AD 来解决此问题,以便运算符评估顺序可以工作,但很笨重。

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


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

C++11 introduces user-defined literals which will allow the introduction of new literal syntax based on existing literals (int, hex, string, float) so that any type will be able to have a literal presentation.

Examples:

// 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

At first glance this looks very cool but I'm wondering how applicable it really is, when I tried to think of having the suffixes _AD and _BC create dates I found that it's problematic due to operator order. 1974/01/06_AD would first evaluate 1974/01 (as plain ints) and only later the 06_AD (to say nothing of August and September having to be written without the 0 for octal reasons). This can be worked around by having the syntax be 1974-1/6_AD so that the operator evaluation order works but it's clunky.

So what my question boils down to is this, do you feel this feature will justify itself? What other literals would you like to define that will make your C++ code more readable?


Updated syntax to fit the final draft on June 2011

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

偏爱你一生 2024-07-15 02:44:21

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

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

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

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

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

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

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

因此,我想这是语言的自然演变,但要尽可能完整:“ 如果你想创建一个类型,并且希望它尽可能像内置类型那样工作,这里有一些工具......

我猜这与 .NET 的决定非常相似使每个基元成为一个结构体,包括布尔值、整数等,并且所有结构体都派生自 Object。 仅这一决定就使 .NET 在处理原语时远远超出了 Java 的能力范围,无论 Java 将在其规范中添加多少装箱/拆箱技巧。

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

这个问题需要来回答。 不是比亚恩·斯特鲁斯特鲁普。 不是赫伯·萨特。 不是 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 都“双精度” “complex”类型和 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
    ^  ^  ^

这是无法避免的,因为 / 作为一个运算符,编译器必须解释它。 而且,据我所知,这是一件好事。

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

"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 日期,但这取决于您的需求。 这就是让用户定义自己的文字名称的全部意义所在。

At first sight, it seems to be simple syntactic sugar.

But when looking deeper, we see it's more than syntactic sugar, as it extends the C++ user's options to create user-defined types that behave exactly like distinct built-in types. In this, this little "bonus" is a very interesting C++11 addition to C++.

Do we really need it in C++?

I see few uses in the code I wrote in the past years, but just because I didn't use it in C++ doesn't mean it's not interesting for another C++ developer.

We had used in C++ (and in C, I guess), compiler-defined literals, to type integer numbers as short or long integers, real numbers as float or double (or even long double), and character strings as normal or wide chars.

In C++, we had the possibility to create our own types (i.e. classes), with potentially no overhead (inlining, etc.). We had the possibility to add operators to their types, to have them behave like similar built-in types, which enables C++ developers to use matrices and complex numbers as naturally as they would have if these have been added to the language itself. We can even add cast operators (which is usually a bad idea, but sometimes, it's just the right solution).

We still missed one thing to have user-types behave as built-in types: user-defined literals.

So, I guess it's a natural evolution for the language, but to be as complete as possible: "If you want to create a type, and you want it to behave as much possible as a built-in types, here are the tools..."

I'd guess it's very similar to .NET's decision to make every primitive a struct, including booleans, integers, etc., and have all structs derive from Object. This decision alone puts .NET far beyond Java's reach when working with primitives, no matter how much boxing/unboxing hacks Java will add to its specification.

Do YOU really need it in C++?

This question is for YOU to answer. Not Bjarne Stroustrup. Not Herb Sutter. Not whatever member of C++ standard committee. This is why you have the choice in C++, and they won't restrict a useful notation to built-in types alone.

If you need it, then it is a welcome addition. If you don't, well... Don't use it. It will cost you nothing.

Welcome to C++, the language where features are optional.

Bloated??? Show me your complexes!!!

There is a difference between bloated and complex (pun intended).

Like shown by Niels at What new capabilities do user-defined literals add to C++?, being able to write a complex number is one of the two features added "recently" to C and 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 ;

Now, both C99 "double complex" type and C++ "std::complex" type are able to be multiplied, added, subtracted, etc., using operator overloading.

But in C99, they just added another type as a built-in type, and built-in operator overloading support. And they added another built-in literal feature.

In C++, they just used existing features of the language, saw that the literal feature was a natural evolution of the language, and thus added it.

In C, if you need the same notation enhancement for another type, you're out of luck until your lobbying to add your quantum wave functions (or 3D points, or whatever basic type you're using in your field of work) to the C standard as a built-in type succeeds.

In C++11, you just can do it yourself:

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

Is it bloated? No, the need is there, as shown by how both C and C++ complexes need a way to represent their literal complex values.

Is it wrongly designed? No, it's designed as every other C++ feature, with extensibility in mind.

Is it for notation purposes only? No, as it can even add type safety to your code.

For example, let's imagine a CSS oriented code:

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 !

It is then very easy to enforce a strong typing to the assignment of values.

Is is dangerous?

Good question. Can these functions be namespaced? If yes, then Jackpot!

Anyway, like everything, you can kill yourself if a tool is used improperly. C is powerful, and you can shoot your head off if you misuse the C gun. C++ has the C gun, but also the scalpel, the taser, and whatever other tool you'll find in the toolkit. You can misuse the scalpel and bleed yourself to death. Or you can build very elegant and robust code.

So, like every C++ feature, do you really need it? It is the question you must answer before using it in C++. If you don't, it will cost you nothing. But if you do really need it, at least, the language won't let you down.

The date example?

Your error, it seems to me, is that you are mixing operators:

1974/01/06AD
    ^  ^  ^

This can't be avoided, because / being an operator, the compiler must interpret it. And, AFAIK, it is a good thing.

To find a solution for your problem, I would write the literal in some other way. For example:

"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

Personally, I would choose the integer and the ISO dates, but it depends on YOUR needs. Which is the whole point of letting the user define its own literal names.

想你的星星会说话 2024-07-15 02:44:21

在这种情况下,使用用户定义的文字而不是构造函数调用有一个优点:

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

优点是运行时异常会转换为编译时错误。
您无法将静态断言添加到采用字符串的位集构造器中(至少不能在没有字符串模板参数的情况下)。

Here's a case where there is an advantage to using user-defined literals instead of a constructor call:

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

The advantage is that a run-time exception is converted to a compile-time error.
You couldn't add the static assert to the bitset ctor taking a string (at least not without string template arguments).

同尘 2024-07-15 02:44:21

这对于数学代码来说非常好。 我突然想到了以下运算符的用途:

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

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

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

It's very nice for mathematical code. Out of my mind I can see the use for the following operators:

deg for degrees. That makes writing absolute angles much more intuitive.

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

It can also be used for various fixed point representations (which are still in use in the field of DSP and graphics).

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

These look like nice examples how to use it. They help to make constants in code more readable. It's another tool to make code unreadable as well, but we already have so much tools abuse that one more does not hurt much.

黯然#的苍凉 2024-07-15 02:44:21

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

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

我只是感到遗憾的是,我们不会在标准中看到一些有用的文字(从外观上看),例如 s 代表 std::string>i 为虚数单位。

UDL 节省的编码时间实际上并没有那么高,但可读性将大大提高,并且越来越多的计算可以转移到编译时以加快执行速度。

UDLs are namespaced (and can be imported by using declarations/directives, but you cannot explicitly namespace a literal like 3.14std::i), which means there (hopefully) won't be a ton of clashes.

The fact that they can actually be templated (and constexpr'd) means that you can do some pretty powerful stuff with UDLs. Bigint authors will be really happy, as they can finally have arbitrarily large constants, calculated at compile time (via constexpr or templates).

I'm just sad that we won't see a couple useful literals in the standard (from the looks of it), like s for std::string and i for the imaginary unit.

The amount of coding time that will be saved by UDLs is actually not that high, but the readability will be vastly increased and more and more calculations can be shifted to compile-time for faster execution.

不及他 2024-07-15 02:44:21

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

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

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

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

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

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

Bjarne Stroustrup talks about UDL's in this C++11 talk, in the first section on type-rich interfaces, around 20 minute mark.

His basic argument for UDLs takes the form of a syllogism:

  1. "Trivial" types, i.e., built-in primitive types, can only catch trivial type errors. Interfaces with richer types allow the type system to catch more kinds of errors.

  2. The kinds of type errors that richly typed code can catch have impact on real code. (He gives the example of the Mars Climate Orbiter, which infamously failed due to a dimensions error in an important constant).

  3. In real code, units are rarely used. People don't use them, because incurring runtime compute or memory overhead to create rich types is too costly, and using pre-existing C++ templated unit code is so notationally ugly that no one uses it. (Empirically, no one uses it, even though the libraries have been around for a decade).

  4. Therefore, in order to get engineers to use units in real code, we needed a device that (1) incurs no runtime overhead and (2) is notationally acceptable.

江南烟雨〆相思醉 2024-07-15 02:44:21

让我添加一些背景信息。 对于我们的工作来说,非常需要用户定义的文字。 我们致力于 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;

但是,C++ 和 C++0x 都不允许这样做,因为字符串被禁止作为模板的参数。 您可以逐个字符地编写名称,但这无疑是一团糟。 使用正确的用户定义文字,我们可以编写类似的内容。 假设我们使用“_n”来标识模型元素名称(我不使用确切的语法,只是为了提出一个想法):

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

最后,将这些定义作为模板可以帮助我们设计用于遍历模型元素、模型转换的算法等等,这确实很高效,因为类型信息、标识、转换等都是由编译器在编译时确定的。

Let me add a little bit of context. For our work, user defined literals is much needed. We work on MDE (Model-Driven Engineering). We want to define models and metamodels in C++. We actually implemented a mapping from Ecore to C++ (EMF4CPP).

The problem comes when being able to define model elements as classes in C++. We are taking the approach of transforming the metamodel (Ecore) to templates with arguments. Arguments of the template are the structural characteristics of types and classes. For example, a class with two int attributes would be something like:

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

Hoever, it turns out that every element in a model or metamodel, usually has a name. We would like to write:

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

BUT, C++, nor C++0x don't allow this, as strings are prohibited as arguments to templates. You can write the name char by char, but this is admitedly a mess. With proper user-defined literals, we could write something similar. Say we use "_n" to identify model element names (I don't use the exact syntax, just to make an idea):

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

Finally, having those definitions as templates helps us a lot to design algorithms for traversing the model elements, model transformations, etc. that are really efficient, because type information, identification, transformations, etc. are determined by the compiler at compile time.

林空鹿饮溪 2024-07-15 02:44:21

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

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、维度的整数幂。

Supporting compile-time dimension checking is the only justification required.

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

assert(energy == 4_J); 

See for example PhysUnits-CT-Cpp11, a small C++11, C++14 header-only library for compile-time dimensional analysis and unit/quantity manipulation and conversion. Simpler than Boost.Units, does support unit symbol literals such as m, g, s, metric prefixes such as m, k, M, only depends on standard C++ library, SI-only, integral powers of dimensions.

厌味 2024-07-15 02:44:21

嗯...我还没有想过这个功能。 您的样本经过深思熟虑,而且确实很有趣。 C++ 和现在一样非常强大,但不幸的是,您阅读的代码片段中使用的语法有时过于复杂。 可读性即使不是全部,也至少是很多。 这样的功能将是为了提高可读性。 如果我举你的最后一个例子

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

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

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

这样也可以。 对于名称较长的类型或您不希望在不编写适配器的情况下拥有如此好的构造函数的类型,它可能是动态隐式对象创建和初始化的一个很好的补充。 另一方面,您也可以使用方法创建和初始化对象。

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

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

啊,我的帖子基本上什么也没说,除了:没关系,影响不会太大:-)。

Hmm... I have not thought about this feature yet. Your sample was well thought out and is certainly interesting. C++ is very powerful as it is now, but unfortunately the syntax used in pieces of code you read is at times overly complex. Readability is, if not all, then at least much. And such a feature would be geared for more readability. If I take your last example

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

... I wonder how you'd express that today. You'd have a KG and a LB class and you'd compare implicit objects:

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

And that would do as well. With types that have longer names or types that you have no hopes of having such a nice constructor for sans writing an adapter, it might be a nice addition for on-the-fly implicit object creation and initialization. On the other hand, you can already create and initialize objects using methods, too.

But I agree with Nils on mathematics. C and C++ trigonometry functions for example require input in radians. I think in degrees though, so a very short implicit conversion like Nils posted is very nice.

Ultimately, it's going to be syntactic sugar however, but it will have a slight effect on readability. And it will probably be easier to write some expressions too (sin(180.0deg) is easier to write than sin(deg(180.0)). And then there will be people who abuse the concept. But then, language-abusive people should use very restrictive languages rather than something as expressive as C++.

Ah, my post says basically nothing except: it's going to be okay, the impact won't be too big. Let's not worry. :-)

洋洋洒洒 2024-07-15 02:44:21

我从来没有需要或想要这个功能(但这可能是 Blub 效果)。 我的本能反应是它很蹩脚,并且可能会吸引那些认为对任何可以远程解释为添加的操作重载operator+很酷的人。

I have never needed or wanted this feature (but this could be the Blub effect). My knee jerk reaction is that it's lame, and likely to appeal to the same people who think that it's cool to overload operator+ for any operation which could remotely be construed as adding.

定格我的天空 2024-07-15 02:44:21

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

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

即使是预期的用途也可能会使阅读源代码变得更加困难:单个字母可能会产生巨大的副作用,而这些副作用是无法从上下文中识别出来的。 由于 u、l 和 f 对称,大多数开发人员会选择单个字母。

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

我看到与“auto”结合的一些优点,也与诸如 增强单位,但不足以值得添加此内容。

然而,我想知道我们想出了什么聪明的主意。

C++ is usually very strict about the syntax used - barring the preprocessor there is not much you can use to define a custom syntax/grammar. E.g. we can overload existing operatos, but we cannot define new ones - IMO this is very much in tune with the spirit of C++.

I don't mind some ways for more customized source code - but the point chosen seems very isolated to me, which confuses me most.

Even intended use may make it much harder to read source code: an single letter may have vast-reaching side effects that in no way can be identified from the context. With symmetry to u, l and f, most developers will choose single letters.

This may also turn scoping into a problem, using single letters in global namespace will probably be considered bad practice, and the tools that are supposed mixing libraries easier (namespaces and descriptive identifiers) will probably defeat its purpose.

I see some merit in combination with "auto", also in combination with a unit library like boost units, but not enough to merit this adition.

I wonder, however, what clever ideas we come up with.

慕烟庭风 2024-07-15 02:44:21

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

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

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

当我放弃 std::string 而转而使用 std::vector 的包装器时,这也很有帮助。

I used user literals for binary strings like this:

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

using std::string(str, n) constructor so that \0 wouldn't cut the string in half. (The project does a lot of work with various file formats.)

This was helpful also when I ditched std::string in favor of a wrapper for std::vector.

迷鸟归林 2024-07-15 02:44:21

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

让我知道,他们是否通过任何示例推断了新语法的添加? 例如,他们是否有几个已经使用 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' - 关键字可能是合理的,但只是也许。 但我们只看 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);

也许? >:)

无论如何,重点是:写 3.14i 而不是 std::complex(0, 3.14); 除了少数超级特殊情况外,总体而言并不会为您节省太多时间。

Line noise in that thing is huge. Also it's horrible to read.

Let me know, did they reason that new syntax addition with any kind of examples? For instance, do they have couple of programs that already use C++0x?

For me, this part:

auto val = 3.14_i

Does not justify this part:

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

Not even if you'd use the i-syntax in 1000 other lines as well. If you write, you probably write 10000 lines of something else along that as well. Especially when you will still probably write mostly everywhere this:

std::complex<double> val = 3.14i

'auto' -keyword may be justified though, only perhaps. But lets take just C++, because it's better than C++0x in this aspect.

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

It's like.. that simple. Even thought all the std and pointy brackets are just lame if you use it about everywhere. I don't start guessing what syntax there's in C++0x for turning std::complex under complex.

complex = std::complex<double>;

That's perhaps something straightforward, but I don't believe it's that simple in C++0x.

typedef std::complex<double> complex;

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

Perhaps? >:)

Anyway, the point is: writing 3.14i instead of std::complex(0, 3.14); does not save you much time in overall except in few super special cases.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文