返回统一初始化引用有效吗?
Is returning uniform initialized reference valid?
此代码示例有效吗?
using ref = char&;
ref foo(ref x) {
return ref{x};
}
int main() {
char a;
foo(a);
return 0;
}
似乎:
- clang 3.5 说是
gcc 4.9 说不
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)...};
}
这似乎是标准中的一个遗漏,GCC 正在执行标准所要求的内容,而 clang 正在执行可能的意图。
来自 C++11(强调我的):
5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
1 A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). [...]
[...]
3 Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a 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
也是一个例外,其中函数转换 不一定 产生纯右值。该标准未能为使用 braced-init-list 到引用类型的功能转换指定类似的异常。因此,在您的示例中,ref{x}
构造了一个 ref
类型的临时对象,直接列表从 {x}
初始化。然后将该临时值视为纯右值,因为这就是标准所说的行为应该是什么,并且该纯右值不能用于绑定到左值引用。
我强烈怀疑如果将此提交给 ISO C++ 委员会,标准将更改为要求 clang 的行为,但根据标准的当前措辞,我认为 GCC 是正确的,至少对于你的具体例子。
您可以省略 ref
(Tp
) 来避免问题,而不是添加变量或切换到括号:
template <typename Tp, typename... Us>
Tp bar(Us&&... us) {
return {std::forward<Us>(us)...};
}
此代码示例有效吗?
using ref = char&;
ref foo(ref x) {
return ref{x};
}
int main() {
char a;
foo(a);
return 0;
}
似乎:
- clang 3.5 说是
gcc 4.9 说不
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)...};
}
这似乎是标准中的一个遗漏,GCC 正在执行标准所要求的内容,而 clang 正在执行可能的意图。
来自 C++11(强调我的):
5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
1 A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). [...]
[...]
3 Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a 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
也是一个例外,其中函数转换 不一定 产生纯右值。该标准未能为使用 braced-init-list 到引用类型的功能转换指定类似的异常。因此,在您的示例中,ref{x}
构造了一个 ref
类型的临时对象,直接列表从 {x}
初始化。然后将该临时值视为纯右值,因为这就是标准所说的行为应该是什么,并且该纯右值不能用于绑定到左值引用。
我强烈怀疑如果将此提交给 ISO C++ 委员会,标准将更改为要求 clang 的行为,但根据标准的当前措辞,我认为 GCC 是正确的,至少对于你的具体例子。
您可以省略 ref
(Tp
) 来避免问题,而不是添加变量或切换到括号:
template <typename Tp, typename... Us>
Tp bar(Us&&... us) {
return {std::forward<Us>(us)...};
}