使用临时函数对象进行全局初始化
Posted
技术标签:
【中文标题】使用临时函数对象进行全局初始化【英文标题】: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()))());
【参考方案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 节。以上是关于使用临时函数对象进行全局初始化的主要内容,如果未能解决你的问题,请参考以下文章