在我理解rvalue引用的过程中,我一直在思考编译器何时确定某个特定的函数参数是一个右值引用,以及它何时将它确定为左值引用。
(此问题与参考折叠有关;请参阅Concise explanation of reference collapsing rules requested: (1) A& & -> A& , (2) A& && -> A& , (3) A&& & -> A& , and (4) A&& && -> A&&)。
特别是,我一直在考虑编译器是否始终将未命名对象视为右值引用和/或编译器总是将临时对象视为右值引用。
反过来,这让我怀疑未命名的对象是否等同于临时对象。
我的问题是:未命名的对象总是暂时的;并且是永远未命名的临时对象?
换句话说:未命名的对象和临时对象是等同于吗?
答案 0 :(得分:3)
我可能错了,因为我不确定“未命名对象”的定义是什么。但请考虑下面的foo()函数的参数:
void foo(int)
{ /* ... */ }
int main()
{ foo(5); }
foo()的论点是未命名的,但它不是暂时的。因此,未命名的对象和临时对象不等效。
答案 1 :(得分:3)
可以命名临时对象。
非常常见的情况 - 作为参数传递给函数时。 另一种不太常见的情况 - 将const引用绑定到函数的右值结果。
int f(int i) { return i + 1; }
int g() { const int &j = f(1); return j; }
未命名的对象通常是临时的,但并非总是如此。例如 - 匿名联合对象:
struct S
{
union { int x; char y; };
} s;
当然,还有operator new
创建的任何对象。
也许还有其他案例,但即使只有这些案例可以作为假设的反例:)
答案 2 :(得分:2)
我一直在思考编译器何时确定某个特定的函数参数是一个右值引用,以及何时它将它确定为左值引用。
我假设您正在讨论具有通用参考参数的功能模板,如下所示?
template<typename T>
void foo(T&& t)
{
}
规则很简单。如果参数是X
类型的右值,则T
将推断为X
,因此T&&
表示X&&
。如果参数是X
类型的左值,那么T
将推断为X&
,因此T&&
表示X& &&
,它会折叠为X&
1}}。
如果您真的在询问参数,那么问题没有多大意义,因为参数永远不是左值引用或右值引用,因为类型X&
的表达式会立即被转换表达式为X
的表达式,表示引用的对象。
但是,如果你真的意味着“编译器如何区分左值参数和右值参数?” (注意缺少的引用),然后答案很简单:编译器知道每个表达式的值类别,因为标准为每个可想到的表达式指定了它的值类别。例如,函数的调用是一个表达式,它可以属于以下三个值类别之一:
X foo(); // the expression foo() is a prvalue
X& bar(); // the expression bar() is an lvalue
X&& baz(); // the expression baz() is an xvalue
(当然,前提是X
本身不是引用类型。)
如果这些都没有回答您的问题,请澄清问题。另外,somewhat relevant FAQ answer。