บทนำ
ISOC ++ 11 (เป็นทางการ ISO / IEC 14882: 2011) เป็นเวอร์ชันล่าสุดของมาตรฐานของภาษาการเขียนโปรแกรม C ++ มันมีคุณสมบัติใหม่และแนวคิดเช่น:
- การอ้างอิงค่า
- xvalue, glvalue, หมวดค่าการแสดงออกของ prvalue
- ย้ายความหมาย
หากเราต้องการที่จะเข้าใจแนวคิดของหมวดค่านิพจน์ใหม่เราต้องระวังว่ามีการอ้างอิง rvalue และ lvalue เป็นการดีกว่าที่จะทราบว่าค่า rvalues สามารถส่งผ่านไปยังการอ้างอิงค่าที่ไม่ใช่ค่า const
int& r_i=7; // compile error
int&& rr_i=7; // OK
เราสามารถรับแนวคิดของหมวดหมู่ค่าได้ถ้าเราอ้างอิงส่วนย่อยที่ชื่อ Lvalues และ rvalues จากร่างการทำงาน N3337 (ร่างที่คล้ายกันมากที่สุดกับมาตรฐาน ISOC ++ 11 ที่เผยแพร่)
3.10 ค่าและ rvalues [basic.lval]
1 นิพจน์ถูกจัดประเภทตามอนุกรมวิธานในรูปที่ 1
- lvalue (เรียกว่าในอดีตเนื่องจาก lvalues อาจปรากฏที่ด้านซ้ายมือของนิพจน์การมอบหมาย) จะกำหนดฟังก์ชันหรือวัตถุ [ตัวอย่าง: ถ้า E เป็นนิพจน์ประเภทตัวชี้ดังนั้น * E คือนิพจน์ lvalue ที่อ้างถึงวัตถุหรือฟังก์ชันที่จุด E นั้น เป็นอีกตัวอย่างหนึ่งผลลัพธ์ของการเรียกใช้ฟังก์ชันที่มีชนิดส่งคืนคือการอ้างอิง lvalue คือ lvalue - ตัวอย่าง]
- ค่า xvalue (ค่า“ eXpiring”) ยังหมายถึงวัตถุซึ่งมักจะใกล้ถึงจุดสิ้นสุดของอายุการใช้งาน (ตัวอย่างเช่นทรัพยากรอาจถูกเคลื่อนย้าย) xvalue คือผลลัพธ์ของนิพจน์บางประเภทที่เกี่ยวข้องกับการอ้างอิง rvalue (8.3.2) [ตัวอย่าง: ผลลัพธ์ของการเรียกใช้ฟังก์ชันที่มีชนิดส่งคืนคือการอ้างอิง rvalue คือ xvalue - ตัวอย่าง]
- glvalue (“ generalized” lvalue) คือ lvalue หรือ xvalue
- rvalue (เรียกว่าในอดีตเนื่องจาก rvalues สามารถปรากฏที่ด้านขวามือของนิพจน์การมอบหมาย) คือ xvalue
วัตถุชั่วคราว (12.2) หรือ subobject หรือค่าที่ไม่
เกี่ยวข้องกับวัตถุ
- prvalue (“ บริสุทธิ์” rvalue) เป็น rvalue ที่ไม่ใช่ xvalue [ตัวอย่าง: ผลลัพธ์ของการเรียกใช้ฟังก์ชันที่มีประเภทส่งคืนไม่ใช่การ
อ้างอิงคือ prvalue ค่าของตัวอักษรเช่น 12, 7.3e5 หรือ
ความจริงก็เป็นที่แพร่หลาย - ตัวอย่าง]
ทุกการแสดงออกเป็นของหนึ่งในการจำแนกประเภทพื้นฐานในอนุกรมวิธานนี้: lvalue, xvalue หรือ prvalue คุณสมบัติของนิพจน์นี้เรียกว่าหมวดหมู่ค่า
แต่ฉันไม่แน่ใจเกี่ยวกับว่าส่วนย่อยนี้เพียงพอที่จะเข้าใจแนวคิดอย่างชัดเจนเพราะ "โดยปกติ" ไม่จริงทั่วไป "ใกล้ถึงจุดสิ้นสุดของอายุการใช้งาน" ไม่เป็นรูปธรรมจริงๆ "เกี่ยวข้องกับการอ้างอิงค่า rvalue" ไม่ชัดเจนจริง ๆ และ "ตัวอย่าง: ผลลัพธ์ของการเรียกใช้ฟังก์ชันที่มีชนิดส่งคืนคือการอ้างอิง rvalue คือ xvalue" ดูเหมือนงูกัดหางของมัน
หมวดหมู่ค่านิยมหลัก
ทุกนิพจน์เป็นของหมวดค่าหลักหนึ่งหมวด หมวดค่าเหล่านี้คือประเภท lvalue, xvalue และ prvalue
lvalues
นิพจน์ E เป็นของหมวดหมู่ lvalue ถ้าหาก E อ้างถึงเอนทิตีที่ ALREADY มีตัวตน (ที่อยู่ชื่อหรือนามแฝง) ที่ทำให้สามารถเข้าถึงได้นอก E
#include <iostream>
int i=7;
const int& f(){
return i;
}
int main()
{
std::cout<<&"www"<<std::endl; // The expression "www" in this row is an lvalue expression, because string literals are arrays and every array has an address.
i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
i; // ... as the entity the expression i in this row refers to.
int* p_i=new int(7);
*p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
*p_i; // ... as the entity the expression *p_i in this row refers to.
const int& r_I=7;
r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
r_I; // ... as the entity the expression r_I in this row refers to.
f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
i; // ... as the entity the expression f() in this row refers to.
return 0;
}
xvalues
นิพจน์ E เป็นของประเภท xvalue ถ้าหากว่าเป็นเท่านั้น
- ผลลัพธ์ของการเรียกใช้ฟังก์ชันไม่ว่าโดยนัยหรือโดยชัดแจ้งซึ่งชนิดส่งคืนคือการอ้างอิง rvalue กับชนิดของวัตถุที่ส่งคืนหรือ
int&& f(){
return 3;
}
int main()
{
f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.
return 0;
}
- การส่งไปยังการอ้างอิง rvalue กับชนิดของวัตถุหรือ
int main()
{
static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).
return 0;
}
- นิพจน์การเข้าถึงของสมาชิกคลาสที่กำหนดสมาชิกข้อมูลที่ไม่คงที่ของประเภทที่ไม่อ้างอิงซึ่งนิพจน์วัตถุนั้นเป็น xvalue หรือ
struct As
{
int i;
};
As&& f(){
return As();
}
int main()
{
f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.
return 0;
}
- นิพจน์ตัวชี้ไปยังสมาชิกซึ่งตัวถูกดำเนินการตัวแรกคือ xvalue และตัวถูกดำเนินการตัวที่สองคือตัวชี้ไปยังข้อมูลสมาชิก
โปรดทราบว่าผลกระทบของกฎด้านบนคือชื่อการอ้างอิง rvalue ไปยังวัตถุนั้นถือว่าเป็น lvalues และการอ้างอิง rvalue ที่ไม่มีชื่อไปยังวัตถุนั้นจะถือว่าเป็น xvalues การอ้างอิง rvalue ไปยังฟังก์ชั่นจะถือว่าเป็น lvalues ไม่ว่าจะตั้งชื่อหรือไม่ก็ตาม
#include <functional>
struct As
{
int i;
};
As&& f(){
return As();
}
int main()
{
f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
As&& rr_a=As();
rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.
return 0;
}
prvalues
นิพจน์ E เป็นของหมวด prvalue ถ้าหาก E นั้นไม่ใช่ของ lvalue หรือในหมวด xvalue
struct As
{
void f(){
this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
}
};
As f(){
return As();
}
int main()
{
f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.
return 0;
}
หมวดหมู่ค่าผสม
มีสองประเภทค่าผสมที่สำคัญเพิ่มเติม หมวดค่าเหล่านี้คือประเภท rvalue และ glvalue
rvalues
นิพจน์ E เป็นของหมวด rvalue ถ้าหาก E นั้นเป็นของประเภท xvalue หรือไปยังหมวด prvalue
โปรดทราบว่าคำจำกัดความนี้หมายความว่านิพจน์ E เป็นของหมวด rvalue ถ้าหาก E อ้างถึงเอนทิตีที่ไม่มีตัวตนใด ๆ ที่ทำให้เข้าถึงได้นอก E YET
glvalues
นิพจน์ E เป็นของหมวดหมู่ glvalue ถ้าหาก E นั้นเป็นของประเภท lvalue หรือไปยังหมวดหมู่ xvalue
กฎปฏิบัติ
Scott Meyer ได้เผยแพร่กฎของหัวแม่มือที่มีประโยชน์มากเพื่อแยกความแตกต่าง rvalues จาก lvalues
- หากคุณสามารถใช้ที่อยู่ของนิพจน์นิพจน์นั้นจะเป็น lvalue
- หากประเภทของการแสดงออกคือการอ้างอิง lvalue (เช่น T & หรือ const T &, ฯลฯ ) การแสดงออกนั้นเป็น lvalue
- มิฉะนั้นการแสดงออกเป็น rvalue แนวคิด (และโดยทั่วไปแล้วในความเป็นจริง) rvalues จะสอดคล้องกับวัตถุชั่วคราวเช่นสิ่งที่ส่งคืนจากฟังก์ชันหรือสร้างขึ้นผ่านการแปลงประเภทโดยนัย ค่าตัวอักษรส่วนใหญ่ (เช่น 10 และ 5.3) ก็มีค่าเช่นกัน