【问题标题】:Why can't you create a 1 statement function without curly braces?为什么不能创建没有大括号的 1 语句函数?
【发布时间】:2014-09-15 11:23:09
【问题描述】:

因此,在大多数编程语言中,如果您使用循环或 if,如果其中只有一条语句,则可以不使用花括号,例如:

if (true)
    //Single statement;

for (int i = 0; i < 10; i++)
    //Single Statement

while (true)
    //Single statement

但是,它不适用于函数,例如:

void myFunction()
    //Single Statement

那么,我的问题是,为什么它不适用于函数?

【问题讨论】:

  • 究竟是哪种语言?还是一般的OOP?
  • @SonerGönül 所有语言都一样
  • "因为这是语言设计者选择编写规范的方式";很可能是因为它很容易把事情弄得一团糟……
  • 没有什么有趣的理由。这就是指定语言的方式。
  • 可能没什么用。如果它是一个非 void 返回函数,那么一个衬里将没有多大用处。这将是编译器检查的额外开销,这可能会导致更长的编译时间。

标签: java c# c++


【解决方案1】:

C++ 需要它来消除一些结构的歧义:

void Foo::bar() const int i = 5;

现在const 属于bar 还是i

【讨论】:

    【解决方案2】:

    因为语言语法禁止你这样做。

    Java 语法定义方法如下:

    MethodDeclaration:
        MethodHeader MethodBody
    

    方法体为:

    MethodBody:
        Block 
        ;
    

    表示块(见下文)或单个分号

    Block:
        { BlockStatementsopt }
    

    一个块作为一个或多个大括号内的语句。

    然而 if 被定义为:

    IfThenStatement:
        if ( Expression ) Statement
    

    关闭后不需要块的地方),因此一行是可以的。

    他们为什么选择这样定义它?只能猜测。

    语法可以在这里找到:http://docs.oracle.com/javase/specs/jls/se7/html/index.html

    【讨论】:

      【解决方案3】:

      这不是一个规则,在某些语言中你可以(Python?是的,我知道这确实是人为的例子:))),在其他语言中你不能。

      您可以很好地将您的问题扩展到类和命名空间,例如,为什么不:

      namespace Example
          class Foo : public Bar
              public: std::string myMethod()
                  return "Oh noes!";
      

      对吗?在每个级别,这只是一个项目,所以为什么不到处跳过大括号呢?

      答案既简单又复杂。

      简单来说,就是可读性。请记住,您可以随意布局代码,因为空格通常会被编译器丢弃

      namespace Example class Foo : public Bar public: std::string myMethod() return "Oh noes!";
      

      嗯,这开始看起来难以理解。请注意,如果您将大括号添加回来

      namespace Example { class Foo : public Bar { public: std::string myMethod() {return "Oh noes!";}}}
      

      然后奇怪的是,它变得有点可理解了。

      真正的问题不在于可读性(谁在乎呢?我当然是在开玩笑),而在于后者:理解力。不仅您必须能够理解代码 - 编译器必须。对于编译器来说,没有“哦,这看起来像函数”这样的东西。编译器必须绝对确定它是一个函数。此外,它必须完全确定它从哪里开始,在哪里结束,等等。而且它必须做到这一点,而不需要过多地查看空格,因为 C 系列语言允许您添加任意数量的空格。

      那么,让我们再看看打包的无括号示例

      namespace Example class Foo : public Bar public : std::string myMethod() return "Oh noes!";
                                  ^                   ^    ^^
      

      我标记了一些有问题的符号。假设您可以定义处理它的语法,请注意“:”字符的含义如何变化。有时它表示您正在指定继承,有时它正在指定对方法的访问修饰符,第三次它只是命名空间限定符。好的,如果你很聪明并注意到它实际上是 '::' 符号,而不仅仅是一个 ':' 字符,则可以丢弃第三个。

      另外,关键字的含义可以改变:

      namespace Example class Foo : public Bar public : std::string myMethod() return "Oh noes!";
                                    ^^^^^^     ^^^^^^
      

      首先,它为继承的基类定义访问修饰符,其次它为方法定义访问修饰符。更何况,第一个不应该后面跟一个“:”,第二个是必须跟它!

      这么多规则、例外和极端情况,我们只介绍了 2 个简单的东西:public 和 ':'。现在,假设您要指定整个语言的语法。你用你想要的方式描述一切。但是,当您将所有规则聚集在一起时,它们在某些时候可能会开始重叠并相互冲突。添加第 N 条规则后,您的“编译器”可能无法判断“公共”是否真正标记了继承,或者启动了一个方法:

      namespace Example class Foo : public ::Bar public : std::string myMethod() return "Oh noes!";
                                    ^^^^^^^^     ^^^^^^^^
      

      请注意,我只将Bar 更改为::Bar。我只添加了一个命名空间限定符,现在我们的“公共后跟冒号”的规则被丢弃了。由于我现在添加了“基类名称可能具有命名空间限定符”的规则,因此我还必须添加更多规则以涵盖另一个极端情况 - 以消除此处“public”和“:”含义的歧义。

      长话短说:规则越多,问题就越多。 “编译器”增长,变慢,消耗更多资源来工作。这会导致无法处理大型代码文件,或者当用户必须等待很长时间才能编译该模块时感到沮丧。

      但对用户来说更糟糕的是,越复杂或模棱两可,错误消息就越糟糕。没有人愿意使用无法解析某些代码并且也无法告诉您它有什么问题的编译器。

      记住在 C++ 中当你忘记一些 ';' 时会发生什么在.h 文件中?或者当您忘记一些} 时?编译器在 30 或 300 行之后向您报告错误。这是因为';'并且 '{}' 可以在很多地方省略,对于那 30 或 300 行,编译器根本不知道这是错误的!如果处处都需要大括号,则可以更快地查明错误点。

      另一种方式:在命名空间、类或函数级别将它们设为可选,将删除基本的块开始/块结束标记,并且至少:

      • 可能会使语法模棱两可(并因此强制添加更多规则)
      • 可能会影响检测(和报告!)错误

      没有人真正想要的任何部分。

      C++ 语法非常复杂,实际上可能根本不可能在这些地方省略大括号。对于 Java 或纯 C,我认为可以制作一个不需要它们的语法/编译器,但它仍然会对错误报告造成很大的伤害。特别是在允许使用#include 和宏的C 语言中。在早期的 Java 中,影响可能较小,因为语法相对简单,与当前的 C++ 相比..

      可能是最简单、最快、最容易实现并且可能最容易学习的语法……几乎在任何地方都需要大括号(或任何其他分隔符)。例如检查 LISP。但是,您的大部分工作将包括不断编写相同的必需标记,这是许多语言用户根本不喜欢的(即,当我需要在 VisualBasic 中使用“如果然后结束如果”来处理一些旧代码时,我会感到恶心“呸)

      现在,如果你看看像 Python 这样的无括号语言 - 他们是如何解决这个问题的?它们通过 .. 意图表示块开始/块结束。在这种语言中,您必须正确缩进您的代码。如果你没有正确缩进它,它根本不会编译,或者循环/函数/等会默默地弄乱他们的代码,因为编译器不知道哪个部分属于哪个范围。这里又没有免费的午餐了。

      【讨论】:

        【解决方案4】:

        基本上,方法(函数)是一组语句,它们组合在一起以执行操作。我们将语句分组以实现可重用。也就是说,如果您知道在这种情况下经常使用一组指令,我们将其创建为一个单独的函数。

        如果你可以在一行代码中完成任务,那你为什么需要写一个函数呢?

        【讨论】:

          【解决方案5】:

          没有理由在编译器中添加额外的解析代码,因为功能真的没用,您编写了多少不是访问器或修改器的单行方法?这已在 C# 中通过属性处理,但尚未在 Java 中处理。

          所以原因是,考虑到大多数开发人员不鼓励省略可选的括号块,它不太可能被使用。

          【讨论】:

            【解决方案6】:

            因为语言的语法不允许你这样做。

            以下是 C 语言中函数的语法,取自 ISO/IEC 9899-1999 规范:

            6.9.1 Function definitions
            Syntax
            1     function-definition:
                      declaration-specifiers declarator declaration-listopt compound-statement
            

            复合语句部分是函数的主体,复合语句被声明为

            compound-statement:
                { block-item-listopt }
            

            即它以大括号开头和结尾。

            ifwhile 或类似的 body 可以有一个 statement 作为它的 body。

             (6.8.5) iteration-statement:
                         while ( expression ) statement
            

            语句可以是多种结构之一。

            statement:
                labeled-statement
                compound-statement
                expression-statement
                selection-statement
                iteration-statement
                jump-statement
            

            其中只有compound-statement 需要大括号。

            【讨论】:

              【解决方案7】:

              在 C++ 中,你需要一个复合语句来创建一个函数体——它实际上被大括号包围。这并不意味着您需要立即使用花括号,以下将编译得很好:

              int foobar()
                  try {
                    return 1;
                  }
                  catch (...){return 0;}
              

              【讨论】:

              • 这将 function-try-block 和普通 try-block 混为一谈。后者是一个语句(但不是复合语句)。前者根本不是声明。
              【解决方案8】:

              你不能准确地说在 C# 中没有一个语句函数。匿名方法可能是其中之一。如果没有单行语句,我们就无法在 c# 中使用 Lambda 表达式。 C# 3.0 将不存在。

              【讨论】:

                猜你喜欢
                • 2022-01-20
                • 1970-01-01
                • 1970-01-01
                • 2013-02-23
                • 1970-01-01
                • 2020-09-17
                • 2011-04-08
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多