以下代码使用g ++ 4.8.1成功编译:
int main()
{
int(*)();
}
它看起来像一个函数指针的简单声明:
int(*f)();
它不能用clang 3.4和vc ++ 2013编译。
它是编译器错误还是标准的黑暗之处?
int(*)();
int(*);
int(*){};
int(*());
Live example with these strange code pieces
更新1: @Ali在评论中添加了一些有趣的信息:
所有4个案例都使用clang 3.5 trunk(202594)进行编译错误,并使用gcc 4.9 trunk(20140302)进行编译。行为与
-std=c++98 -pedantic
相同,但int(*){};
除外,这是可以理解的;扩展初始值设定项列表仅适用于-std=c++11
。
更新2:正如@CantChooseUsernames中提到的his answer一样,即使初始化它们仍然可以正常编译,并且g ++(无论是否初始化)都没有为它们生成汇编没有任何启用的优化:
int(*)() = 0;
int(*) = 0;
int(*){} = 0;
int(*()) = 0;
Live example with initializations
更新3:我很惊讶地发现int(*)() = "Hello, world!";
编译得很好(当然int(*p)() = "Hello, world!";
无法编译)。
更新4:这很棒,但int(*){} = Hello, world!;
编译得很好。以下非常奇怪的代码:int(*){}() = -+*/%&|^~.,:!?$()[]{};
(live example)。
更新5:在@zwol中注明his comment
这个和一些相关的句法问题被跟踪为gcc bug 68265。
答案 0 :(得分:15)
根据C ++标准(第7节声明中的第6页)
6 init-declarator-list 中的每个init-declarator都包含 一个declarator-id ,这是由它声明的名称 init-declarator,因此声明声明的名称之一
所以它只是一个编译器错误。
有效代码可能看起来像(除了你显示的函数指针声明),虽然我无法用我的MS VC ++ 2010编译它。
int(*p){};
您用于测试的编译器似乎允许声明而没有声明者ID。
还要考虑8.1节类型名称
的以下段落1明确指定类型转换,并作为参数 sizeof,alignof,new或typeid ,类型的名称应为 指定。这可以使用type-id来完成,它在语法上是一个 声明该类型的变量或函数,省略了 实体名称。
答案 1 :(得分:7)
我不确定这有多大帮助,但我尝试了以下内容(铿锵3.3,g ++ 4.8.1):
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
另一方面,g ++ 4.8.2和4.9.0中的所有内容都很好。不幸的是,我没有铿锵声.3。
非常粗略,声明[iso section 7]按顺序包含以下部分:
static
,virtual
)const double
,vector<int>
)n
,*p
,a[7]
,f(int)
)const
,noexcept
)= {1,2,3}
或{ return 0; }
现在,声明符大致由一个名称和一些声明符运算符[iso 8/4]组成。
前缀运算符,例如:
*
(指针)*const
(常量指针)&
(左值参考)&&
(右值参考)auto
(函数返回类型,尾随时)后缀运算符,例如:
[]
(数组)()
(功能)->
(函数尾随返回类型)上述运算符旨在反映它们在表达式中的用法。 Postfix运算符绑定比前缀更紧密,括号可用于更改它们的顺序:int *f()
是一个返回指针int
的函数,而int (*f)()
是指向返回{{1}的函数的指针。 1}}。
也许我错了,但我认为这些运营商不能在声明中没有名字。因此,当我们编写int
时,int *q;
是基类型,而int
是由前缀运算符*q
组成的声明符,后跟名称*
。但是q
本身不能出现。
另一方面,当我们定义int *;
时,声明using Q = int*;
本身就没问题,因为Q;
是基本类型。当然,因为我们没有声明任何内容,我们可能会根据编译器选项获得错误或警告,但这是一个不同的错误。
以上只是我的理解。标准(例如N3337)所说的是[iso 8.3 / 1]:
每个声明者只包含一个 declarator-id ;它命名声明的标识符。 declarator-id 中出现的 unqualified-id 应该是一个简单的标识符,除了一些特殊函数的声明(12.3 [用户定义的转换],12.4 [析构函数],13.5 [重载运算符])以及模板特化或部分特化的声明(14.7)。
(方括号中的注释是我的)。所以我理解Q
应该是无效的,我不能说为什么它在clang和不同版本的g ++中有不同的行为。
答案 2 :(得分:6)
您可以使用:http://gcc.godbolt.org/查看程序集..
int main()
{
int(*)() = 0;
return 0;
}
生成:
main:
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
popq %rbp
ret
相当于:int main() {return 0;}
因此,即使没有优化,gcc也不会为它生成程序集。它应该发出警告或错误吗?我没有任何线索,但它并不关心或为未命名的func指针做任何事情。
然而:
int main()
{
int (*p)() = 0;
return 0;
}
没有优化会产生:
main:
pushq %rbp
movq %rsp, %rbp
movq $0, -8(%rbp)
movl $0, %eax
popq %rbp
ret
在堆栈上分配8个字节..