【问题标题】:Global initialization with temporary function object使用临时函数对象进行全局初始化
【发布时间】:2013-02-06 15:03:44
【问题描述】:

以下代码

#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 中的错误还是 Clang 的扩展/错误?

【问题讨论】:

  • Fwiw,这两个都吐了(在 Intellisense 中,错误:不允许强制转换为类型“random_device()”和“)”预期表达式)仍然可以使用 VS2012 和Nov2012-CTP。
  • 您可以使用新式构造函数语法强制构造函数调用(而不是奇怪的类型解释):std::mt19937 generator { std::random_device{} () };
  • 添加另一个括号也可以:std::mt19937 generator(((std::random_device()))());

标签: c++ gcc c++11 clang


【解决方案1】:

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:

演员表

  • 一元表达式
  • ( type-id ) cast-expression

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

后缀表达式

  • [...]
  • 后缀表达式 (表达式列表opt)
  • [...]

后缀表达式(std::random_device())表达式列表被省略。

我已经在 gcc bugzilla 上提交了http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56239,看起来应该很快就会解决。

请注意,如果您向 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 通用初始化语法)是添加另一层括号,因为 type-id 不能包含外括号:

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

【讨论】:

  • 那么,Clang 的成功是非标准行为?另外,std::random_device () 类型是什么意思?
  • @lvella clang 是正确的;该表达式可以通过语法明确解析,因此 gcc 无法解析它是不正确的。 std::random_device() 类型是不带参数并返回 std::random_device 的函数的函数类型。
  • 我以为是std::random_device (*)(),仅此而已。
  • @lvella std::random_device (*)() 是一个函数 pointer 类型。函数类型很难看到,也不经常使用,但它们是类型系统的一部分,例如函数指针类型的形式为指向 F 的指针,其中 F 是函数类型。
【解决方案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的解析是错误的。

【讨论】:

  • OP 写了B b ( (A())() );
  • @jrok:嗯,实际上你是对的。那么我是否发现了一个不相关的错误?
  • 但是不能解析成A()是一个类型(函数返回A),所以我们调用它的默认构造函数A()()
  • @zch 值初始化语法 T() 不允许将 T 括起来。请参阅第 5.2.3 节。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-17
  • 1970-01-01
  • 2010-12-04
  • 2011-02-26
  • 1970-01-01
  • 2020-12-19
相关资源
最近更新 更多