首页 文章

我对值初始化的尝试被解释为函数声明,为什么不是A a(());解决这个问题?

提问于
浏览
145

Stack Overflow教给我的很多东西都是所谓的“最令人烦恼的解析”,经典地用一条线来证明

A a(B()); //declares a function

虽然这对于大多数人来说,直观地看起来是 A 类型的对象 a 的声明,将临时 B 对象作为构造函数参数,它实际上是一个函数 a 的声明,返回一个 A ,获取一个返回函数的指针 B 并且本身不带参数 . 同样的线

A a(); //declares a function

也属于同一类别,因为它不是一个对象,而是声明一个函数 . 现在,在第一种情况下,这个问题的通常解决方法是在 B() 周围添加一组额外的括号/括号,因为编译器会将其解释为对象的声明

A a((B())); //declares an object

但是,在第二种情况下,执行相同操作会导致编译错误

A a(()); //compile error

我的问题是,为什么?是的我'm very well aware that the correct '解决方法'是将它更改为 A a; ,但我很想知道第一个例子中额外的 () 为编译器做了什么,然后在第二个例子中重新应用它时不起作用 . A a((B())); 解决方法是否将特定异常写入标准?

5 回答

  • 6

    C函数声明符

    首先,有C.在C中, A a() 是函数声明 . 例如, putchar 具有以下声明 . 通常,这样的声明存储在头文件中,但是如果您知道函数声明的样子,那么没有什么能阻止您手动编写它们 . 参数名称在声明中是可选的,因此我在此示例中省略了它 .

    int putchar(int);
    

    这允许您编写这样的代码 .

    int puts(const char *);
    int main() {
        puts("Hello, world!");
    }
    

    C还允许您定义将函数作为参数的函数,具有良好的可读语法,看起来像函数调用(好吧,它是可读的,只要您不返回指向函数的指针) .

    #include <stdio.h>
    
    int eighty_four() {
        return 84;
    }
    
    int output_result(int callback()) {
        printf("Returned: %d\n", callback());
        return 0;
    }
    
    int main() {
        return output_result(eighty_four);
    }
    

    正如我所提到的,C允许在头文件中省略参数名称,因此 output_result 在头文件中看起来像这样 .

    int output_result(int());
    

    构造函数中的一个参数

    你不认识那个吗?好吧,让我提醒你 .

    A a(B());
    

    是的,这是完全相同的功能声明 . Aintaoutput_resultBint .

    您可以轻松地注意到C与C的新功能之间的冲突 . 确切地说,构造函数是类名和括号,而替代声明语法是 () 而不是 = . 按照设计,C试图与C代码兼容,因此它必须处理这种情况 - 即使几乎没人关心 . 因此,旧C功能优先于新C功能 . 声明语法尝试将名称作为函数进行匹配,然后如果失败则使用 () 恢复为新语法 .

    如果其中一个特性不存在,或者具有不同的语法(如C11中的 {} ),则对于具有一个参数的语法,此问题永远不会发生 .

    现在你可能会问为什么 A a((B())) 有效 . 好吧,让我们用无用的括号声明 output_result .

    int output_result((int()));
    

    它不会起作用 . 语法要求变量不在括号中 .

    <stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
    

    但是,C期望标准表达式 . 在C中,您可以编写以下代码 .

    int value = int();
    

    以下代码 .

    int value = ((((int()))));
    

    C期望括号内的表达式是... well ...表达式,而不是C期望的类型 . 括号在这里没有任何意义 . 但是,通过插入无用的括号,C函数声明不匹配,并且可以正确匹配新语法(只需要一个表达式,例如 2 + 2 ) .

    构造函数中的更多参数

    当然有一个论点很好,但两个呢?并不是构造函数可能只有一个参数 . 带有两个参数的内置类之一是 std::string

    std::string hundred_dots(100, '.');
    

    这一切都很好(从技术上讲,如果它被写为 std::string wat(int(), char()) ,它会有最令人烦恼的解析,但让's be honest - who would write that? But let'假设这个代码有一个棘手的问题 . 你会认为你必须把所有东西放在括号中 .

    std::string hundred_dots((100, '.'));
    

    不是这样 .

    <stdin>:2:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
    In file included from /usr/include/c++/4.8/string:53:0,
                     from <stdin>:1:
    /usr/include/c++/4.8/bits/basic_string.tcc:212:5: error:   initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
         basic_string<_CharT, _Traits, _Alloc>::
         ^
    

    我不确定为什么g试图将 char 转换为 const char * . 无论哪种方式,只使用一个 char 类型的值调用构造函数 . 没有重载有一个 char 类型的参数,因此编译器很困惑 . 您可能会问 - 为什么参数是char类型?

    (100, '.')
    

    是的, , 这里是一个逗号运算符 . 逗号运算符接受两个参数,并给出右侧参数 . 对于我的解释,我不知道这件事 .

    相反,要解决最令人烦恼的解析,需要以下代码 .

    std::string hundred_dots((100), ('.'));
    

    参数在括号中,而不是整个表达式 . 事实上,只有一个表达式需要在括号中,因为它足以从C语法略微突破到使用C功能 . 事情将我们带到零论点 .

    构造函数中的零参数

    你可能已经在我的解释中注意到了 eighty_four 函数 .

    int eighty_four();
    

    是的,这也受到最令人烦恼的解析的影响 . 这是一个有效的定义,如果您创建了头文件,那么您很可能已经看过(并且您应该) . 添加括号不会解决问题 .

    int eighty_four(());
    

    为什么会这样?好吧, () 不是表达 . 在C中,您必须在括号之间放置一个表达式 . 你不能在C中写 auto value = () ,因为 () 不是't mean anything (and even if did, like empty tuple (see Python), it would be one argument, not zero). Practically that means you cannot use shorthand syntax without using C++11' s {} 语法,因为没有表达式放在括号中,并且函数声明的C语法将始终适用 .

  • 66

    此问题的最终解决方案是,如果可以,请转到C 11统一初始化语法 .

    A a{};
    

    http://www.stroustrup.com/C++11FAQ.html#uniform-init

  • 11

    您的示例中最内层的parens将是一个表达式,在C语法中,语法将 expression 定义为 assignment-expression 或另一个 expression ,后跟逗号和另一个 assignment-expression (附录A.4 - 语法摘要/表达式) .

    语法进一步将 assignment-expression 定义为几种其他类型的表达式之一,其中没有一种可以是空的(或只有空格) .

    所以你不能拥有 A a(()) 的原因仅仅是因为语法没有回答为什么创建C的人没有想到如果有合理的替代方案他们宁愿不提出这么特殊的情况 .

  • 33

    没有开明的答案,只是因为它没有被C语言定义为有效的语法......所以它是如此,根据语言的定义 .

    如果你有一个表达式,那么它是有效的 . 例如:

    ((0));//compiles
    

    更简单的说法:因为 (x) 是有效的C表达式,而 () 不是 .

    要了解有关如何定义语言以及编译器如何工作的更多信息,您应该了解Formal language theory或更具体的Context Free Grammars (CFG)以及有限状态机等相关资料 . 如果你对此感兴趣,虽然维基百科页面赢了't be enough, you' ll必须得到一本书 .

  • 27

    你可以改为

    A a(());
    

    使用

    A a=A();
    

相关问题