给出以下代码:
#include <string>
void foo()
{
std::string s(std::move(""));
}
这与Apple clang(xcode 7)编译,并且不与visual studio 2015一起生成以下错误:
error C2440: 'return': cannot convert from 'const char [1]' to 'const char (&&)[1]'
note: You cannot bind an lvalue to an rvalue reference
main.cpp(4): note: see reference to function template instantiation 'const char (&&std::move<const char(&)[1]>(_Ty) noexcept)[1]' being compiled
with
[
_Ty=const char (&)[1]
]
暂时忽略移动是多余的,在这种情况下哪个标准库实现更正确?
我的感觉是""
的类型为const char[1]
,因此std::move
应返回std::remove_reference<const char[1]&>::type&&
const char[1]&&
。
在我看来,这应该衰减到const char*
。
或者我是否误解了规则?
答案 0 :(得分:11)
这看起来像一个Visual Studio错误。这归结为std::move,如果我们查看cppreference页面,它会有以下签名:
template< class T >
typename std::remove_reference<T>::type&& move( T&& t );
然后它返回:
static_cast<typename std::remove_reference<T>::type&&>(t)
匹配草案C ++标准部分20.2.4
转发/移动帮助者[转发]。
使用code I grabbed from here我们可以看到以下示例:
#include <iostream>
template<typename T>
struct value_category {
// Or can be an integral or enum value
static constexpr auto value = "prvalue";
};
template<typename T>
struct value_category<T&> {
static constexpr auto value = "lvalue";
};
template<typename T>
struct value_category<T&&> {
static constexpr auto value = "xvalue";
};
// Double parens for ensuring we inspect an expression,
// not an entity
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value
int main()
{
std::cout << VALUE_CATEGORY( static_cast<std::remove_reference<const char[1]>::type&&>("") ) << std::endl ;
}
使用Wandbox从gcc和clang生成以下答案:
xvalue
以及使用webcompiler的Visual Studio中的答案:
lvalue
因此Visual Studio对原始代码的错误:
您不能将左值绑定到右值参考
当它尝试将static_cast<typename std::remove_reference<T>::type&&>(t)
的结果绑定到std::remove_reference<T>::type&&
时,std::move
的返回值。
我没有看到任何理由static_cast
应该像在Visual Studio案例中那样生成左值。
答案 1 :(得分:4)
让我们首先将其分为两部分,以便我们分别对每一部分进行分析:
#include <string>
void foo()
{
auto x = std::move("");
std::string s(x);
}
第二部分,从x
(无论可能是什么类型)初始化字符串,这不是真正的问题或问题。手头的问题(至少在我看来)是第一行,我们尝试将rvalue引用绑定到字符串文字。
标准的相关部分是[dcl.init.ref]/5
(§8.5.3/ 5,至少在我见过的C ++标准的大多数版本中)。
首先是:
对类型'' cv1 T1''的引用由类型为'' cv2 T2''的表达式初始化,如下所示。
接下来是一个子弹列表。第一项仅涵盖左值引用,因此我们将忽略它。第二项说:
如果初始化表达式为
- 是一个xvalue(但不是位字段)类prvalue,数组prvalue或函数lvalue [...]
- 具有班级类型(即T2
是班级类型)[...]
- 除此以外 - 如果T1
或T2
是类类型且T1不是,则不是与T2相关的引用<...]
显然,这些都不适用。字符串文字不是xvalue,类prvalue,数组prvalue或函数lvalue,也不是类类型。
仅留下:
如果
T1
与T2
的参考相关:
- cv1 应与 cv2 相同的cv资格,或更高的cv资格,以及
- 如果引用是右值引用,则初始化表达式不应为左值。
由于在这种情况下,编译器推导出转换结果的类型,因此将与初始化程序的类型相关联。在这种情况下,正如我强调的部分所说,初始化表达式不能是左值。
这只留下字符串文字是否是左值的问题。至少随便,我不能立即找到C ++标准的部分,说它们是(在字符串文字部分没有提到它)。如果它不存在,下一步将是查看基本文档(C标准),该文档明确指出字符串文字是左值(N1570,§6.5.1/ 4)
字符串文字是主要表达式。它是一个左值类型,详见6.4.5。
我希望我能在C ++标准中找到直接的声明(我很确定它应该存在),但无论出于何种原因我现在都找不到它。