C ++ decltype และวงเล็บ - ทำไม


32

มีการพูดถึง เรื่องนี้มาก่อนแต่นี่ไม่ได้ซ้ำกัน

เมื่อมีคนถามถึงความแตกต่างระหว่างdecltype(a)และdecltype((a))คำตอบปกติคือ - aเป็นตัวแปร(a)คือการแสดงออก ฉันพบคำตอบนี้ไม่พอใจ

อย่างแรกaคือการแสดงออกเช่นกัน ตัวเลือกสำหรับการแสดงออกหลักได้แก่ หมู่คนอื่น ๆ -

  • (นิพจน์)
  • ID-แสดงออก

ที่สำคัญกว่านั้นการใช้ถ้อยคำสำหรับเดคไทป์จะพิจารณาวงเล็บอย่างชัดเจนมาก :

For an expression e, the type denoted by decltype(e) is defined as follows:
(1.1)  if e is an unparenthesized id-expression naming a structured binding, ...
(1.2)  otherwise, if e is an unparenthesized id-expression naming a non-type template-parameter, ...
(1.3)  otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, ...
(1.4)  otherwise, ...

ดังนั้นคำถามยังคงอยู่ ทำไมวงเล็บจึงได้รับการปฏิบัติต่างกัน? มีใครคุ้นเคยกับเอกสารทางเทคนิคหรือการอภิปรายของคณะกรรมการหลัง? การพิจารณาอย่างชัดเจนสำหรับวงเล็บนำไปสู่การคิดว่านี่ไม่ใช่การกำกับดูแลดังนั้นจะต้องมีเหตุผลทางเทคนิคที่ฉันพลาด


2
"คำตอบปกติคือ - a คือตัวแปร (a) คือนิพจน์"สิ่งที่พวกเขาหมายถึงคือ " (a)คือนิพจน์และaเป็นนิพจน์และตัวแปร"
HolyBlackCat

คำตอบ:


18

มันไม่ใช่การกำกับดูแล เป็นเรื่องที่น่าสนใจในDecltype และ auto (รุ่นที่ 4) (N1705 = 04-0145)มีข้อความ:

ตอนนี้กฎของ decltype ระบุอย่างชัดเจนว่าdecltype((e)) == decltype(e)(ตามที่ EWG แนะนำ)

แต่ในDecltype (การแก้ไข 6): ถ้อยคำที่เสนอ (N2115 = 06-018)การเปลี่ยนแปลงอย่างใดอย่างหนึ่งคือ

วงเล็บแสดงออกภายใน decltype id-expressionจะไม่ถือว่าเป็น

ไม่มีเหตุผลในการใช้ถ้อยคำ แต่ฉันคิดว่านี่เป็นส่วนขยายของชนิดของเดคชนิดที่ใช้ไวยากรณ์ที่แตกต่างกันเล็กน้อยกล่าวอีกอย่างหนึ่งก็คือมันมีจุดประสงค์เพื่อแยกความแตกต่างของกรณีเหล่านี้

การใช้งานที่แสดงใน C ++ draft9.2.8.4:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17;        // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

สิ่งที่น่าสนใจจริงๆคือวิธีการทำงานกับreturnข้อความ:

decltype(auto) f()
{
    int i{ 0 };
    return (i);
}

My Visual Studio 2019 แนะนำให้ฉันลบวงเล็บที่ซ้ำซ้อน แต่จริงๆแล้วพวกเขาเปลี่ยนdecltype((i))เป็นการเปลี่ยนค่าส่งคืนint&ซึ่งทำให้ UB นับตั้งแต่กลับมาอ้างอิงถึงตัวแปรท้องถิ่น


ขอบคุณสำหรับการชี้ต้นกำเนิด! ไม่เพียง แต่จะมีการระบุประเภทของ Dectype ต่างกัน แต่กลับกลายเป็นว่าเริ่มต้นแล้ว เอกสาร rev-6 เพิ่มพฤติกรรมวงเล็บตลกนี้อย่างชัดเจน แต่ข้ามเหตุผล :( ฉันเดาว่าใกล้เคียงกับคำตอบที่เราได้รับตอนนี้ ..
Ofek Shilon

แต่ถึงกระนั้นก็ไม่มีใครเสนอให้แก้ไขปัญหาที่โง่เง่าโดยทำให้มันเป็นคำค้นหาสองคำที่แตกต่างกัน หนึ่งในความผิดพลาดที่ใหญ่ที่สุดของคณะกรรมการ (ซึ่งในอดีตทำผิดพลาดโดยไม่ได้ตั้งใจ)
curiousguy

13

ทำไมวงเล็บจึงได้รับการปฏิบัติต่างกัน?

วงเล็บไม่ได้รับการปฏิบัติแตกต่างกัน เป็นนิพจน์ id ที่ไม่ยึดติดที่ได้รับการปฏิบัติแตกต่างกัน

เมื่อมีวงเล็บอยู่จะมีการใช้กฎปกติสำหรับนิพจน์ทั้งหมด decltypeชนิดและประเภทค่าจะถูกสกัดและการประมวลผลในรูปแบบของ

มีข้อกำหนดพิเศษเพื่อให้เราสามารถเขียนโค้ดที่มีประโยชน์ได้ง่ายขึ้น เมื่อนำdecltypeไปใช้กับชื่อของตัวแปร (สมาชิก) เราไม่ต้องการประเภทที่แสดงถึงคุณสมบัติของตัวแปรเมื่อถือว่าเป็นนิพจน์ แต่เราต้องการเพียงแค่ตัวแปรที่มีการประกาศด้วยโดยไม่ต้องใช้คุณลักษณะหลายประเภทเพื่อให้ได้มันมา และนั่นคือสิ่งที่decltypeระบุไว้เพื่อให้เรา

ถ้าเราสนใจคุณสมบัติของตัวแปรในการแสดงออกเราก็ยังสามารถรับมันได้อย่างง่ายดายโดยมีวงเล็บเป็นคู่


ดังนั้นสำหรับintสมาชิกiของa, decltype(a.i)อยู่ในintขณะที่decltype((a.i))เป็นint&(สมมติว่าaไม่ได้const)? ตั้งแต่นิพจน์a.iสามารถกำหนดได้?
n314159

1
@ n314159 - นั่นคือแก่นแท้ของมัน การแสดงออกa.iเป็น lvalue ไม่ใช่ const เพื่อให้คุณได้รับไม่ใช่ const (a.i)ชนิดการอ้างอิงสำหรับ
StoryTeller - Unslander Monica

ทำไม 'กฎปกติสำหรับนิพจน์ทั้งหมด' ระบุว่าประเภทของพวกเขาคือข้อมูลอ้างอิง ทำไม 'คุณสมบัติของตัวแปรเป็นนิพจน์' จึงรวมคุณสมบัติของการอ้างอิงไว้ด้วย? มันเป็นค่าเริ่มต้นที่เป็นธรรมชาติหรือไม่?
Ofek Shilon

1
@OfekShilon - ประเภทของพวกเขาไม่ใช่ข้อมูลอ้างอิง ประเภทของการแสดงออกไม่เคยวิเคราะห์เป็นข้อมูลอ้างอิง แต่ dectlype สามารถแก้ไขเป็นประเภทได้เท่านั้นและมันหมายถึงการบอกเราไม่เพียง แต่ประเภทของนิพจน์เท่านั้น หมวดค่าถูกประมวลผลตามประเภทการอ้างอิง lvalues ​​คือ&xvalues ​​คือ&&และ prvalues ​​ไม่ใช่ประเภทการอ้างอิง
StoryTeller - Unslander Monica

@StoryTeller แน่นอนไม่มีความแตกต่าง 'ธรรมชาติ' ระหว่างประเภทของนิพจน์และนิพจน์ที่ไม่ใช่ ซึ่งทำให้ความแตกต่างของเทียม
Ofek Shilon

1

ภาษา C ++ 11 ล่วงหน้าต้องการเครื่องมือเพื่อรับข้อมูลสองประเภท :

  • ประเภทของการแสดงออก
  • ชนิดของตัวแปรตามที่ประกาศไว้

เนื่องจากลักษณะของข้อมูลนี้จึงต้องเพิ่มคุณสมบัติในภาษา (ไม่สามารถทำได้ในห้องสมุด) นั่นหมายถึงคำหลักใหม่ มาตรฐานอาจมีคำหลักใหม่สองคำสำหรับสิ่งนี้ ตัวอย่างเช่นexprtypeการรับชนิดของนิพจน์และdecltypeเพื่อรับชนิดการประกาศของตัวแปร นั่นจะเป็นตัวเลือกที่ชัดเจนและมีความสุข

อย่างไรก็ตามคณะกรรมการมาตรฐานพยายามอย่างหนักที่สุดเพื่อหลีกเลี่ยงการแนะนำคำหลักใหม่เป็นภาษาเพื่อลดการแตกหักของรหัสเก่า ความเข้ากันได้ย้อนหลังเป็นปรัชญาหลักของภาษา

ดังนั้นด้วย C ++ 11 decltypeเราได้เพียงแค่หนึ่งคำที่ใช้สำหรับสองสิ่งที่แตกต่างกัน: วิธีที่แตกต่างระหว่างการใช้งานสองแบบคือการปฏิบัติdecltype(id-expression)แตกต่างกัน มันเป็นการตัดสินใจที่มีสติโดยคณะกรรมการประนีประนอม (เล็ก)


ฉันจำได้ว่าได้ยินสิ่งนี้ในการพูดคุยของ CPP อย่างไรก็ตามฉันไม่มีความหวังในการค้นหาแหล่งที่มา มันจะดีถ้ามีคนพบมัน
bolov

1
C ++ ที่ผ่านมามีการเพิ่มคำหลักใหม่จำนวนมากโดยที่ไม่มีขีดล่างและบางคำมีคำภาษาอังกฤษหนึ่งคำ เหตุผลนั้นไร้สาระจริงๆ
curiousguy

@coolguy คำหลักทุกคำที่นำเสนอจะขัดแย้งกับสัญลักษณ์ผู้ใช้ที่มีอยู่ดังนั้นคณะกรรมการจึงให้น้ำหนักอย่างมากในการตัดสินใจเพิ่มคำหลักที่สงวนไว้ใหม่ให้กับภาษา มันไม่ใช่เรื่องไร้สาระ
bolov

ไม่จริง: exportถูกนำมาใช้ ถ้าคุณสามารถมีexport(ก่อนหน้านี้แม่ทุกคนโดยเริ่มต้น "ส่งออก") คุณสามารถมีสิ่งที่ชอบและdecltype constexprการเพิ่มอย่างชัดเจนregisterในภาษาอื่นจะเป็นปัญหา
curiousguy

@bolov ขอบคุณ! (1) การเก็งกำไรหรือความรู้นี้หรือไม่? คุณทราบหรือไม่ว่าการสนทนาที่การหลีกเลี่ยงคำหลักพิเศษเป็นแรงจูงใจ (2) คุณสามารถยกตัวอย่างที่ id-expression จริง ๆ แล้วจะต้องได้รับการปฏิบัติแตกต่างกันหรือไม่ถ้ามันถูกใช้ "เป็น expression" (สิ่งนี้หมายความว่าอย่างไร?)
Ofek Shilon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.