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

GRB*_*GRB 154 c++

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

A a(B()); //declares a function
Run Code Online (Sandbox Code Playgroud)

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

A a(); //declares a function
Run Code Online (Sandbox Code Playgroud)

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

A a((B())); //declares an object
Run Code Online (Sandbox Code Playgroud)

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

A a(()); //compile error
Run Code Online (Sandbox Code Playgroud)

我的问题是,为什么?是的我非常清楚正确的"解决方法"是将其更改为A a;,但我很想知道()第一个示例中的额外功能是什么,然后在重新应用它时不起作用第二个例子.A a((B()));变通办法是否是写入标准的特定异常?

Bri*_*ndy 70

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

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

 ((0));//compiles
Run Code Online (Sandbox Code Playgroud)

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

要了解有关如何定义语言以及编译器如何工作的更多信息,您应该了解正式语言理论或更具体的上下文无关语法(CFG)和相关材料(如有限状态机).如果你对此感兴趣,虽然维基百科页面还不够,你将不得不得到一本书.

  • 更简单的说:因为`(x)`是一个有效的C++表达式,而`()`不是. (45认同)

Dav*_*vid 37

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

A a{};
Run Code Online (Sandbox Code Playgroud)

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


Kon*_*ski 29

C函数声明符

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

int putchar(int);
Run Code Online (Sandbox Code Playgroud)

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

int puts(const char *);
int main() {
    puts("Hello, world!");
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

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

int output_result(int());
Run Code Online (Sandbox Code Playgroud)

构造函数中的一个参数

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

A a(B());
Run Code Online (Sandbox Code Playgroud)

是的,这是完全相同的功能声明.Aint,aoutput_result,Bint.

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

如果其中一个特性不存在,或者具有不同的语法(如{}在C++ 11中),那么对于带有一个参数的语法,这个问题就永远不会发生.

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

int output_result((int()));
Run Code Online (Sandbox Code Playgroud)

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

<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
Run Code Online (Sandbox Code Playgroud)

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

int value = int();
Run Code Online (Sandbox Code Playgroud)

以下代码.

int value = ((((int()))));
Run Code Online (Sandbox Code Playgroud)

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

构造函数中的更多参数

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

std::string hundred_dots(100, '.');
Run Code Online (Sandbox Code Playgroud)

这一切都很好(从技术上讲,如果它被写成,它会有最令人烦恼的解析std::string wat(int(), char()),但是说实话 - 谁会写出来?但是我们假设这个代码有一个棘手的问题.你会认为你必须把它括号中的一切.

std::string hundred_dots((100, '.'));
Run Code Online (Sandbox Code Playgroud)

不是这样.

<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>::
     ^
Run Code Online (Sandbox Code Playgroud)

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

(100, '.')
Run Code Online (Sandbox Code Playgroud)

是的,,这是一个逗号运算符.逗号运算符接受两个参数,并给出右侧参数.它并不是真的有用,但它是我的解释所知.

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

std::string hundred_dots((100), ('.'));
Run Code Online (Sandbox Code Playgroud)

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

构造函数中的零参数

你可能已经eighty_four在我的解释中注意到了这个功能.

int eighty_four();
Run Code Online (Sandbox Code Playgroud)

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

int eighty_four(());
Run Code Online (Sandbox Code Playgroud)

为什么会这样?好吧,()不是表达.在C++中,您必须在括号之间放置一个表达式.你不能用auto value = ()C++ 编写,因为()它没有任何意义(即使它像空元组一样(参见Python),它将是一个参数,而不是零).实际上,这意味着您不能在不使用C++ 11的语法的情况下使用简写语法{},因为没有表达式放在括号中,并且函数声明的C语法将始终适用.


use*_*149 12

你可以改为

A a(());
Run Code Online (Sandbox Code Playgroud)

使用

A a=A();
Run Code Online (Sandbox Code Playgroud)

  • "更好的解决方法"并不等同.`int a = int();`用0初始化`a`,`int a;`使`a`初始化为未初始化.一个正确的解决方法是使用`A a = {};`用于聚合,`A a;`当默认初始化执行你想要的东西时,`A a = A();`在所有其他情况下 - 或者只是使用`A a = A();`一致.在C++ 11中,只需使用`A a {};` (32认同)
  • 已经提到了一个更好的解决方法:`A a;` (11认同)

Mic*_*urr 6

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

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

所以你不能拥有的原因A a(())仅仅是因为语法不允许它.但是,我不能回答为什么创建C++的人不允许这种特殊用途的空白parens作为某种特殊情况 - 我猜他们宁愿不提出这样一个特殊情况,如果有的话一个合理的选择.