C++ 用g++;

C++ 用g++;,c++,gcc,c++11,g++,C++,Gcc,C++11,G++,以下代码使用g++4.8.1成功编译: int main() { int(*)(); } using P = int(*)(); using Q = int*; P; // warning only Q; // warning only int(*)(); // error (but only in clang) int*; // error int(*p)(); // ok int *q; // ok 它看起来像一个指向函数的指针的简单声明: int(*f)();

以下代码使用g++4.8.1成功编译:

int main()
{
    int(*)();
}
using P = int(*)();
using Q = int*;
P; // warning only
Q; // warning only
int(*)(); // error (but only in clang)
int*;     // error
int(*p)(); // ok
int *q;    // ok
它看起来像一个指向函数的指针的简单声明:

int(*f)();
它不是用Clang3.4和vc++2013编译的

这是一个编译器错误还是标准的一个黑暗的地方


使用g++4.8.1编译良好的类似奇怪代码段列表(更新):
  • int(*)()

  • int(*)

  • int(*){}

  • int(*())

  • 更新1:在评论中添加了一些有趣的信息:

    所有4种情况下,使用Clang3.5主干(202594)会出现编译错误,使用GCC4.9主干(20140302)会出现编译错误。行为与
    -std=c++98-pedantic
    相同,除了
    int(*){}这是可以理解的;扩展初始值设定项列表仅适用于
    -std=c++11

    更新2:如中所述,即使进行了初始化,它们仍然可以很好地编译,并且g++不会为它们生成任何程序集(无论是否进行初始化),即使没有启用任何优化:

  • int(*)()=0

  • int(*)=0

  • int(*){}=0

  • int(*())=0

  • 更新3:我真的很惊讶地发现
    int(*)(=“你好,世界!”也可以编译(而
    int(*p)(=“你好,世界!”;
    当然不能编译)

    更新4:这太棒了,但是
    int(*){}=Hello,world编译良好。还有下面一段非常奇怪的代码:
    int(*){}()=-+*/%&^~,:$()[]{};()

    更新5:如中所述

    这一点以及一些相关的语法问题正在作为gcc进行跟踪


    根据C++标准(第7节声明的P<6)< /P> 6初始化声明器列表中的每个初始化声明器都包含 一个声明器id,它是该声明器声明的名称 init声明程序,因此是声明声明的名称之一

    因此,这只是一个编译器错误

    有效的代码可能看起来像示例(除了您显示的函数指针声明),尽管我不能用MS VC++2010编译它

    int(*p){};
    
    您用于测试的编译器似乎允许在没有声明器id的情况下进行声明

    还应考虑第8.1节类型名称的以下段落

    1显式指定类型转换,并作为的参数 sizeof、alignof、new或typeid,类型名称应为 明确规定。这可以通过类型id来完成,该类型id在语法上是 省略 实体的名称


    我不确定这有多大帮助,但我尝试了以下方法(clang 3.3,g++4.8.1):

    另一方面,在g++4.8.2和4.9.0中,一切都可以很好地编译。不幸的是,我没有叮当声3.4

    非常粗略地说,声明[iso第7节]按顺序由以下部分组成:

  • 可选前缀说明符(例如
    静态
    虚拟
  • 基本类型(例如,
    constdouble
    vector
  • 声明符(例如
    n
    *p
    a[7]
    f(int)
  • 可选后缀函数说明符(例如
    const
    noexcept
  • 可选初始值设定项或函数体(例如
    ={1,2,3}
    {return 0;}
  • 现在,声明器大致由一个名称和一些可选的声明器操作符组成[iso 8/4]

    前缀运算符,例如:

    • *
      (指针)
    • *const
      (常量指针)
    • (左值参考)
    • &&
      (右值参考)
    • auto
      (函数返回类型,尾随时)
    后缀运算符,例如:

    • []
      (阵列)
    • ()
      (函数)
    • ->
      (函数尾部返回类型)
    上述运算符旨在反映它们在表达式中的用法。后缀运算符的绑定比前缀更紧,括号可用于更改它们的顺序:
    int*f()
    是返回
    int
    指针的函数,而
    int(*f)(
    是返回
    int
    指针的函数

    也许我错了,但我认为这些运算符不能在没有名称的声明中。因此,当我们编写
    int*q;
    时,
    int
    是基本类型,
    *q
    是由前缀运算符
    *
    后跟名称
    q
    组成的声明符。但是
    int*;
    本身不能出现

    另一方面,当我们使用Q=int*;
    定义
    时,声明
    Q;
    本身就可以了,因为
    Q
    是基本类型。当然,因为我们没有声明任何内容,我们可能会得到错误或警告,具体取决于编译器选项,但这是一个不同的错误

    以上只是我的理解。标准(如N3337)所说的是[iso 8.3/1]:

    每个声明器只包含一个声明器id;它命名声明的标识符。声明器id中出现的非限定id应为简单标识符,但某些特殊函数的声明除外(12.3[用户定义转换]、12.4[析构函数]、13.5[重载运算符])以及模板专门化或部分专门化的声明(14.7)

    (方括号中的注释是我的)。因此我理解
    int(*)();
    应该是无效的,我不能说为什么它在clang和不同版本的g++中有不同的行为。

    您可以使用这个:查看程序集

    int main()
    {
        int(*)() = 0;
        return 0;
    }
    
    生成:

    main:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $0, %eax
        popq    %rbp
        ret
    
    这相当于:
    intmain(){return
    
    int main()
    {
        int (*p)() = 0;
        return 0;
    }
    
    main:
        pushq   %rbp
        movq    %rsp, %rbp
        movq    $0, -8(%rbp)
        movl    $0, %eax
        popq    %rbp
        ret