大括号初始化与括号错误

Brace-initialization vs. Parenthesis Bug

我为此提交了一个 GCC 错误,但我宁愿仔细检查一下。

考虑以下程序:

#include <utility>
template<typename T, typename A>
void F(A&& a) { T(std::forward<A>(a)); } // Note: () syntax.
int main() { int i; F<int&>(i); }

和:

#include <utility>
template<typename T, typename A>
void F(A&& a) { T{std::forward<A>(a)}; } // Note: {} syntax.
int main() { int i; F<int&>(i); }

最新的 Clang 和 MSVC 编译器接受这两个程序。 GCC 5 及更高版本接受第一个程序但拒绝第二个程序,声称 invalid cast of an rvalue expression of type 'int' to type 'int&'.

这是 GCC 错误吗?或者这确实是上述上下文中 T{}T() 之间的区别(因此是 Clang 和 MSVC 中的错误)?

编辑:

问题可以缩小为以下更简单的摘录:

int i; (int&){i};

int i; (int&)(i);

有两个不同的问题:

  • 标准不清楚 T{x} 应该为引用类型 T 做什么。目前 [expr.type.conv]/1 says that it creates a prvalue of type T, which is nonsense for reference types. This is core issue 1521.
  • 理智的事情可能是让 T{x} 对于引用类型 T 大致做 T __tmp{x}; 然后产生等价于 static_cast<T>(__tmp) (所以右值引用的 xvalue T 和左值引用的左值 T)。然而,发布的 C++11 搞砸了引用列表初始化的规范,使其总是创建一个临时的。结果是 int i; int &r{i}; 编译失败,因为它会尝试将 r 绑定到 i 的临时副本,这显然是无稽之谈。这是由 core issue 1288 修复的,它的分辨率 GCC 应该实现,但从错误消息来看它似乎没有完全修复。