为什么 "decltype(i+j)" 的结果不是右值引用?
Why is the result of "decltype(i+j)" not an rvalue reference?
我正在尝试为产生右值的操作提出一个简单的示例。
这个测试用例应该有效,但令我惊讶的是(对我来说),添加两个 int
的结果不是右值(参考)。我在这里错过了什么?
void test(int i, int j)
{
// this assert should pass, but fails:
static_assert(std::is_same<decltype(i + j), int&&>(), "i + j should be a rvalue");
// this assert passed, but should fail:
static_assert(std::is_same<decltype(i + j), int>(), "this assert should fail...");
}
i + j
是 prvalue expression,
A prvalue ("pure rvalue") expression is an expression that does not have identity and can be moved from.
a + b, a % b, a & b, a << b, and all other built-in arithmetic expressions;
不是 xvalue,
An xvalue ("expiring value") expression is an expression that has identity and can be moved from.
并且 decltype specifier 对 prvalue 产生 T
,而不是 T&&
。
a) if the value category of expression is xvalue, then decltype yields T&&;
b) if the value category of expression is lvalue, then decltype yields T&;
c) if the value category of expression is prvalue, then decltype yields T.
你可以通过 std::move
:
static_assert(std::is_same<decltype(std::move(i + j)), int&&>(), "std::move(i + j) is a xvalue then this static_assert won't fail");
根据@songyuanyao的,我注意到我的错误是检查错了:我的意图是检查i+j
的结果是否会绑定到 右值引用,但我检查它是否 是 右值引用。
decltype deduces the type based on the value category, not based on what reference type 值将 绑定到 :
1) if the value category of expression is xvalue
, then decltype yields T&&
;
2) if the value category of expression is lvalue
, then decltype yields T&
;
3) if the value category of expression is prvalue
, then decltype yields T
.
如列表所示,自 C++11 起,rvalues
不再作为最低级别的独立类别存在。 它们现在是复合类别包含 prvalues
和 xvalues
的类别。所写的问题询问表达式是否为 rvalue reference
并检查它是否为 xvalue
。
从上面的列表可以看出,i+j
是一个prvalue
,所以适用第三种情况。这解释了为什么 decltype(i + j)
是 int
而不是 int&&
。 xvalues
和 prvalues
都绑定到 右值引用。
因此,通过检查 i+j
是否绑定到 lvalue reference
或 rvalue reference
确认,确实,它 绑定到 rvalue reference
:
void foo(const int& f)
{
std::cout << "binds to lvalue reference" << std::endl;
}
void foo(int&& f)
{
std::cout << "binds to rvalue reference" << std::endl;
}
void test(int i, int j)
{
foo(i); // lvalue -> lvalue ref
foo(std::move(i)); // xvalue -> rvalue ref
// (std::move converts the argument to a rvalue reference and returns it as an xvalue)
foo(i + j); // prvalue -> rvalue ref
}
结论: i+j
不是右值引用,但它绑定到一个.
我正在尝试为产生右值的操作提出一个简单的示例。
这个测试用例应该有效,但令我惊讶的是(对我来说),添加两个 int
的结果不是右值(参考)。我在这里错过了什么?
void test(int i, int j)
{
// this assert should pass, but fails:
static_assert(std::is_same<decltype(i + j), int&&>(), "i + j should be a rvalue");
// this assert passed, but should fail:
static_assert(std::is_same<decltype(i + j), int>(), "this assert should fail...");
}
i + j
是 prvalue expression,
A prvalue ("pure rvalue") expression is an expression that does not have identity and can be moved from.
a + b, a % b, a & b, a << b, and all other built-in arithmetic expressions;
不是 xvalue,
An xvalue ("expiring value") expression is an expression that has identity and can be moved from.
并且 decltype specifier 对 prvalue 产生 T
,而不是 T&&
。
a) if the value category of expression is xvalue, then decltype yields T&&;
b) if the value category of expression is lvalue, then decltype yields T&;
c) if the value category of expression is prvalue, then decltype yields T.
你可以通过 std::move
:
static_assert(std::is_same<decltype(std::move(i + j)), int&&>(), "std::move(i + j) is a xvalue then this static_assert won't fail");
根据@songyuanyao的i+j
的结果是否会绑定到 右值引用,但我检查它是否 是 右值引用。
decltype deduces the type based on the value category, not based on what reference type 值将 绑定到 :
1) if the value category of expression is
xvalue
, then decltype yieldsT&&
;
2) if the value category of expression islvalue
, then decltype yieldsT&
;
3) if the value category of expression isprvalue
, then decltype yieldsT
.
如列表所示,自 C++11 起,rvalues
不再作为最低级别的独立类别存在。 它们现在是复合类别包含 prvalues
和 xvalues
的类别。所写的问题询问表达式是否为 rvalue reference
并检查它是否为 xvalue
。
从上面的列表可以看出,i+j
是一个prvalue
,所以适用第三种情况。这解释了为什么 decltype(i + j)
是 int
而不是 int&&
。 xvalues
和 prvalues
都绑定到 右值引用。
因此,通过检查 i+j
是否绑定到 lvalue reference
或 rvalue reference
确认,确实,它 绑定到 rvalue reference
:
void foo(const int& f)
{
std::cout << "binds to lvalue reference" << std::endl;
}
void foo(int&& f)
{
std::cout << "binds to rvalue reference" << std::endl;
}
void test(int i, int j)
{
foo(i); // lvalue -> lvalue ref
foo(std::move(i)); // xvalue -> rvalue ref
// (std::move converts the argument to a rvalue reference and returns it as an xvalue)
foo(i + j); // prvalue -> rvalue ref
}
结论: i+j
不是右值引用,但它绑定到一个.