使用临时函数对象进行全局初始化

Global initialization with temporary function object

本文关键字:全局 初始化 对象 函数      更新时间:2023-10-16

以下代码

#include <random>
std::mt19937 generator((std::random_device())());

只编译带有clang的文件:

$ clang++ -c -std=c++0x test.cpp

但使用gcc:失败

$ g++ -c -std=c++0x test.cpp 
test.cpp:3:47: erro: expected primary-expression before ‘)’ token

该代码在C++11中有效吗?这是GCC中的bug还是Clang的扩展/bug?

gcc正在将子表达式(std::random_device())()解析为函数类型std::random_device()的强制转换。它有助于查看icc的错误输出,它的信息量略高于gcc的:

source.cpp(6): error: cast to type "std::random_device ()" is not allowed
  std::mt19937 generator((std::random_device())());
                          ^
source.cpp(6): error: expected an expression
  std::mt19937 generator((std::random_device())());
                                                ^

相关产量为5.4p2:

强制转换表达式

  • 一元表达式
  • 类型id强制转换表达式

由于一对空括号()不是一元表达式,因此该生成不可用,编译器应从5.2p1:中选择生成

后缀表达式

  • […]
  • 后缀表达式表达式列表opt
  • […]

其中后缀表达式(std::random_device()),并且省略表达式列表

我已提交http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56239关于gcc bugzilla,它看起来应该很快得到解决。

请注意,如果向operator()提供参数,则8.2p2要求编译器将表达式解析为强制转换,即使强制转换为函数类型是非法的(如果有多个参数,则使用逗号运算符将参数列表解析为表达式

(std::less<int>())(1, 2);
^~~~~~~~~~~~~~~~~~ illegal C-style cast
 ^~~~~~~~~~~~~~~~ type-id of function type std::less<int>()
                  ^~~~~~ argument of C-style cast
                    ^ comma operator

写这篇文章的正确方法(除了使用C++11通用初始化器语法)是添加另一层括号,因为类型id不能包含外括号:

((std::less<int>()))(1, 2);

GCC处理函数声明的方式似乎有问题。举个例子:

struct A
{
    bool operator () () { return true; }
};
struct B
{
    B(bool) { }
};
B b((         // This cannot be parsed as a function declaration,
    A()()     // and yet GCC 4.7.2 interprets it as such:
    ));       // "error: 'type name' declared as function returning 
              // a function B b((A()()));"
int main() { }

由于A()()周围存在额外的括号,因此语法形式B b(( A()() ));不能被解析为函数的声明。

您的问题示例中的声明略有不同:

B b(
   (A())()
   );

然而,即使在这种情况下,(A())()也不能被解释为返回返回A的函数的类型(总是试图将b视为具有未命名参数的函数声明)。所以问题是:它能被解释为其他东西吗?如果是这样,并且在这种情况下有意义,那么编译器应该考虑将整个表达式解析为对象b的构造。

这可能是GCC和Clang不同意的根本点:

int main()
{
    (A())(); // OK for Clang, ERROR for GCC
}

除了试图构造A类型的临时并调用其调用运算符之外,我看不出以上内容如何被解释为其他内容。它不能是函数声明,因为如果A被解释为返回类型,那么名称就会丢失(反之亦然)。

另一方面,(A())是用于创建类型为A的临时的有效表达式,该临时支持调用运算符(其返回类型与B的构造函数接受的类型相同)。因此,(A())()应该是bool类型的有效表达式。

出于这个原因,我认为GCC的解析是错误的。