此代码示例是否有效?
using ref = char&;
ref foo(ref x) {
return ref{x};
}
int main() {
char a;
foo(a);
return 0;
}
似乎:
gcc 4.9说NO
main.cpp: In function 'char& foo(ref)':
main.cpp:4:15: error: invalid cast of an rvalue expression of type 'char' to type 'ref {aka char&}'
return ref{x};
^
http://coliru.stacked-crooked.com/a/cb6604b81083393f
那么哪个编译器是对的?还是未指明?
通过以下方式克服gcc构建错误非常容易:
使用括号代替大括号
ref foo(ref x) {
return ref(x);
}
命名返回值
ref foo(ref x) {
ref ret{x};
return ret;
}
选项1.打破统一初始化,选项2.添加无用的代码行。
这里已经有类似的问题了: Why can't I initialize a reference in an initializer list with uniform initialization?
但提到pr50025已在gcc 4.9中修复。
我知道上面的代码示例相当无用, 但我故意过分简化它以指出问题。 在现实生活中,代码问题可以隐藏在通用函数中,如:
#include <utility>
template <typename Tp, typename... Us>
Tp bar(Us&&... us) {
return Tp{std::forward<Us>(us)...};
}
答案 0 :(得分:3)
这似乎是标准中的遗漏,GCC正在严格执行标准所要求的内容,并且clang正在寻找可能的目标。
来自C ++ 11(强调我的):
5.2.3显式类型转换(功能表示法)[expr.type.conv]
1 简单类型说明符(7.1.6.2)或 typename-specifier (14.6),后跟带括号的表达式列表给定表达式列表构造指定类型的值。如果表达式列表是单个表达式,则类型转换表达式与相应的强制转换表达式(5.4)等效(在定义中,如果在含义中定义)。 [...]
[...]
3同样,简单类型说明符或 typename-specifier 后跟 braced-init-list 会创建一个临时对象指定的类型direct-list-initialized(8.5.4)与指定的 braced-init-list ,,其值是临时对象作为prvalue 。
对于 braced-init-list 的情况,标准并没有指定它的工作方式就像C风格的转换。它没有:
typedef char *cp;
int main() {
int i;
(cp(&i)); // okay: C-style casts can be used as reinterpret_cast
(cp{&i}); // error: no implicit conversion from int * to char *
}
不幸的是,T(expr)
等同于(T)expr
也是一个例外,其中功能强制转换不会必然会产生prvalue。标准未能为使用braced-init-list到引用类型的功能强制转换指定类似的异常。因此,在您的示例中,ref{x}
构造了一个ref
类型的临时文件,从{x}
开始直接列表初始化。然后将该临时值视为prvalue,因为该标准表示该行为应该是什么,并且prvalue不能用于绑定到左值引用。
我强烈怀疑,如果将其提交给ISO C ++委员会,标准将被更改为要求clang的行为,但根据标准的当前措辞,我认为它是GCC这是正确的,至少对你的具体例子而言。
您可以省略ref
(Tp
)来避免此问题,而不是添加变量或切换到括号:
template <typename Tp, typename... Us>
Tp bar(Us&&... us) {
return {std::forward<Us>(us)...};
}