12

我用 GCC、Clang、ICC 和 VS 测试了以下代码:

void f() {}
  
void g(void (&&)()) { }

int main() {
    g(f);
}

正如我们所看到的,g它接受一个右值引用,但f它是一个左值,通常,右值引用不能绑定到左值。这正是ICC所抱怨的:

error: an rvalue reference cannot be bound to an lvalue

VS 也给出了一个错误,但出于另一个原因:

error C2664: 'void h(void (__cdecl &&)(void))' : cannot convert parameter 1 from 'void (__cdecl *)(void)' to 'void (__cdecl &&)(void)'

这表明 VS 会立即执行函数到指针的转换,而不是直接将引用绑定到f. 值得一提的是,如果我替换g(f)g(&f)then 四个编译器会产生同样的错误。

最后,GCC 和 Clang 接受了代码,我相信它们是正确的。我的推理基于 8.5.3/5

对“cv1 T1”类型的引用由“cv2 T2”类型的表达式初始化为

— 如果引用是左值引用 [...]

— 否则,[...]引用应为右值引用

     — 如果初始化表达式是[...]函数左值[...]

     然后引用绑定到初始化表达式的值[...]

我的解释是否正确(即 Clang 和 GCC 因给定原因而合规)?

4

1 回答 1

9

Is my interpretation correct [...]?

Yes.

Your interpretation is correct because of the Paragraph of the Standard that you quoted. A further confirmation comes from Paragraph 13.3.3.1.4/3 on reference binding:

Except for an implicit object parameter, for which see 13.3.1, a standard conversion sequence cannot be formed if it requires binding an lvalue reference other than a reference to a non-volatile const type to an rvalue or binding an rvalue reference to an lvalue other than a function lvalue. [...]

Paragraph 13.3.3.2/3 contains a further (indirect) confirmation:

[...] Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if

— [...]

— S1 and S2 are reference bindings (8.5.3) and S1 binds an lvalue reference to a function lvalue and S2 binds an rvalue reference to a function lvalue. [ Example:

int f(void(&)()); // #1
int f(void(&&)()); // #2
void g();
int i1 = f(g); // calls #1

—<em>end example ]

于 2013-03-23T17:49:11.727 回答