เดคไทป์ (อัตโนมัติ) มีประโยชน์อะไรบ้าง?


151

ใน c ++ 14 มีการใช้decltype(auto)สำนวน

โดยปกติการใช้งานของมันคือการอนุญาตให้มีautoการประกาศใช้decltypeกฎระเบียบเกี่ยวกับการแสดงออกที่กำหนด

ค้นหาตัวอย่างของการใช้คำว่า "ดี" ของสำนวนฉันสามารถนึกถึงสิ่งต่าง ๆ ดังต่อไปนี้ (โดยสก็อตต์เมเยอร์ส ) นั่นคือสำหรับการคืนค่าประเภทของฟังก์ชัน :

template<typename ContainerType, typename IndexType>                // C++14
decltype(auto) grab(ContainerType&& container, IndexType&& index)
{
  authenticateUser();
  return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}

มีตัวอย่างอื่น ๆ ที่คุณสมบัติภาษาใหม่นี้มีประโยชน์หรือไม่


2
โพสต์นี้โดยทั่วไปแนะนำให้พยายามหลีกเลี่ยงสำนวนนี้เพราะเมื่อใช้คุณจะให้ตัวเลือกน้อยลงสำหรับการเพิ่มประสิทธิภาพให้กับคอมไพเลอร์stackoverflow.com/a/20092875/2485710
user2485710

ฉันเคยใช้decltype(auto)สำหรับสิ่งที่คล้ายกับtemplate<class U, V> decltype(auto) first(std::pair<U, V>& p) { return p.first; }แต่ฉันก็ตระหนักว่าฉันต้องใช้return (p.first);ซึ่งทำงานได้อย่างน่าประหลาดใจ (แต่ IIRC นี้ตั้งใจไว้)
dyp

@ user2485710 ไม่แน่ใจว่ามันเกี่ยวกับการเพิ่มประสิทธิภาพโดยเฉพาะอย่างยิ่งมีโอกาสเกิดอุบัติเหตุมากขึ้นหากdecltype(auto)สามารถทำให้สิ่งที่จะคัดลอก / ย้ายไปยังวัตถุที่ประกาศประกาศตอบโต้กับความคาดหวัง
underscore_d

คำตอบ:


170

ส่งคืนชนิดการส่งต่อในรหัสทั่วไป

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

auto const& Example(int const& i) 
{ 
    return i; 
}

แต่ในรหัสทั่วไปคุณต้องการส่งต่อชนิดส่งคืนได้อย่างสมบูรณ์โดยไม่ทราบว่าคุณกำลังติดต่อกับการอ้างอิงหรือค่า decltype(auto)ให้ความสามารถนั้นแก่คุณ:

template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args) 
{ 
    return fun(std::forward<Args>(args)...); 
}

การหน่วงเวลาการหักชนิดส่งคืนในเท็มเพลตแบบเรียกซ้ำ

ในนี้ Q & Aไม่กี่วันที่ผ่านมามีการเรียกซ้ำอนันต์ระหว่างแม่แบบ instantiation ถูกพบเมื่อประเภทการกลับมาของแม่แบบที่ถูกระบุเป็นแทนdecltype(iter(Int<i-1>{}))decltype(auto)

template<int i> 
struct Int {};

constexpr auto iter(Int<0>) -> Int<0>;

template<int i>
constexpr auto iter(Int<i>) -> decltype(auto) 
{ return iter(Int<i-1>{}); }

int main() { decltype(iter(Int<10>{})) a; }

decltype(auto)ใช้ที่นี่เพื่อชะลอการหักค่าส่งคืนหลังจากฝุ่นของแม่แบบอินสแตนซ์ได้ตัดสิน

การใช้งานอื่น ๆ

นอกจากนี้คุณยังสามารถใช้decltype(auto)ในบริบทอื่น ๆ เช่นร่างมาตรฐานN3936ยังรัฐ

7.1.6.4 ข้อมูลจำเพาะอัตโนมัติ [dcl.spec.auto]

1 ตัวระบุประเภทautoและdecltype(auto)กำหนดประเภทตัวยึดที่จะถูกแทนที่ในภายหลังโดยการหักจาก initializer หรือโดยการระบุอย่างชัดเจนด้วย trailing-return-type autoFi เอ้อชนิด speci ยังใช้เพื่อหมายว่าแลมบ์ดาเป็นแลมบ์ดาทั่วไป

2 ประเภทตัวยึดสามารถปรากฏกับ declarator ฟังก์ชั่นใน decl-ระบุ-seq, ชนิดระบุ-seq แปลงฟังก์ชั่น-ID หรือต่อท้ายผลตอบแทนชนิดในบริบทใด ๆ ที่เช่น declarator เป็นที่ถูกต้อง ถ้าฟังก์ชั่น declarator รวมถึง trailing-return-type (8.3.5) นั่นจะระบุประเภทการส่งคืนที่ประกาศของฟังก์ชัน หากชนิดส่งคืนที่ประกาศของฟังก์ชันมีประเภทตัวยึดตำแหน่งประเภทส่งคืนของฟังก์ชันจะถูกหักจากคำสั่งส่งคืนในเนื้อหาของฟังก์ชันหากมี

แบบร่างยังมีตัวอย่างของการกำหนดค่าเริ่มต้นตัวแปรนี้:

int i;
int&& f();
auto x3a = i;                  // decltype(x3a) is int
decltype(auto) x3d = i;        // decltype(x3d) is int
auto x4a = (i);                // decltype(x4a) is int
decltype(auto) x4d = (i);      // decltype(x4d) is int&
auto x5a = f();                // decltype(x5a) is int
decltype(auto) x5d = f();      // decltype(x5d) is int&&
auto x6a = { 1, 2 };           // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i;                // decltype(x7a) is int*
decltype(auto)*x7d = &i;       // error, declared type is not plain decltype(auto)

17
พฤติกรรมที่แตกต่างของ(i)vs iสิ่งใหม่ใน C ++ 14 หรือไม่
Danvil

14
@Danvil decltype(expr)และdecltype((expr))แตกต่างใน C ++ 11 อยู่แล้วนี่เป็นการสรุปพฤติกรรมดังกล่าว
TemplateRex

13
ฉันเพิ่งได้เรียนรู้สิ่งนี้รู้สึกเหมือนเป็นการตัดสินใจที่แย่มาก ... เพิ่มความแตกต่างนิดหน่อยในความหมายทางไวยากรณ์ของวงเล็บ
Kahler

ตัวอย่างที่แจ้งให้เกิดความขยะแขยงนี้อยู่เสมอคือไวยากรณ์แบบหนึ่งไฟล์ต่อหนึ่งสตริง (ยังกล่าวถึงในลิงก์นั้น) ทุกส่วนดูเหมือนจะย้อนหลัง คุณอาจไม่ได้คาดหวังความกำกวมเลยและลบวงเล็บที่ซ้ำซ้อนออกจากกลุ่มตัวอย่าง คุณคาดหวังความคลุมเครือในการแก้ไขโดยกระบวนการกำจัดตาม SFINAE แต่ผู้สมัครที่ไม่ใช่ผู้ประกาศจะถูกกำจัดล่วงหน้า (SF คือ AE); และด้วยความหงุดหงิดคุณอาจเดินหน้าต่อไปทันทีที่มันรวบรวมความคิดว่า parens ตามอำเภอใจแก้ปัญหาความกำกวม แต่พวกเขาแนะนำมัน ฉันคิดว่าส่วนใหญ่ที่รบกวนอาจารย์ CS101
จอห์น P

@TemplateRex: เกี่ยวกับการหน่วงเวลาการแก้ปัญหาชนิดส่งคืนในคำถามที่อ้างถึง: เท่าที่ฉันเห็นในสถานการณ์เฉพาะง่าย ๆautoก็สามารถทำงานได้ดีเช่นกันเนื่องจากผลลัพธ์ถูกส่งคืนโดยค่าต่อไป ... หรือฉันพลาด อะไร?
Aconcagua

36

ข้อความจากที่นี่ :

  • decltype(auto)มีประโยชน์อย่างยิ่งสำหรับการหักประเภทการส่งคืนของฟังก์ชันการส่งต่อและตัวห่อที่คล้ายกันซึ่งคุณต้องการให้ประเภทนั้น "ติดตาม" การแสดงออกบางอย่างที่คุณเรียกใช้

  • ตัวอย่างเช่นกำหนดฟังก์ชั่นด้านล่าง:


   string  lookup1();
   string& lookup2();

  • ใน C ++ 11 เราสามารถเขียนฟังก์ชั่น wrapper ต่อไปนี้ซึ่งจำไว้ว่าต้องรักษา reference-ness ของ return type:

   string  look_up_a_string_1() { return lookup1(); }
   string& look_up_a_string_2() { return lookup2(); }

  • ใน C ++ 14 เราสามารถทำให้:

   decltype(auto) look_up_a_string_1() { return lookup1(); }
   decltype(auto) look_up_a_string_2() { return lookup2(); }

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

  • โดยเฉพาะอย่างยิ่งแม้ว่าจะสามารถใช้เพื่อประกาศตัวแปรท้องถิ่นการทำเช่นนั้นอาจเป็นเพียงปฏิปักษ์เนื่องจากการอ้างอิงของตัวแปรท้องถิ่นไม่ควรขึ้นอยู่กับนิพจน์การเริ่มต้น

  • นอกจากนี้ยังมีความอ่อนไหวต่อวิธีที่คุณเขียนคำสั่ง return

  • ตัวอย่างเช่นสองฟังก์ชันด้านล่างมีประเภทผลตอบแทนต่างกัน:


   decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
   decltype(auto) look_up_a_string_2() { auto str = lookup2(); return(str); }

  • ผลตอบแทนแรกstringผลตอบแทนที่สองซึ่งมีการอ้างอิงถึงตัวแปรท้องถิ่นstring&str

จากข้อเสนอที่คุณสามารถเห็นการใช้งานที่ตั้งใจมากขึ้น


3
ทำไมไม่ใช้autoเพื่อผลตอบแทน?
BЈовић

@ BЈовићสามารถทำงานกับการลดประเภทการส่งคืนทั่วไป (เช่นการautoส่งคืน) ได้เช่นกัน แต่ OP ขอให้ใช้เป็นdecltype(auto)พิเศษ
101010

3
คำถามนี้ยังเกี่ยวข้องกัน ประเภทผลตอบแทนเป็นauto lookup_a_string() { ... } อย่างไร มันเป็นประเภทที่ไม่ใช่การอ้างอิงเสมอหรือไม่? และดังนั้นจึงauto lookup_a_string() ->decltype(auto) { ... }จำเป็นต้องบังคับให้อนุญาตให้มีการอ้างอิงถึง (ในบางกรณี) กลับมา?
Aaron McDaid

@AaronMcDaid Deductible autoถูกกำหนดในเทมเพลตของ pass by value ดังนั้นใช่มันไม่สามารถอ้างอิงได้ โปรดรอautoได้ทุกอย่างรวมถึงข้อมูลอ้างอิงด้วย
curiousguy

4
std::vectorกล่าวขวัญตัวอย่างเพิ่มเติมมูลค่าจะกลับองค์ประกอบของหนึ่ง template<typename T> struct S { auto & operator[](std::size_t i) { return v[i]; } std::vector<T> v; }สมมติว่าคุณมี จากนั้นจะกลับอ้างอิงห้อยเพราะความเชี่ยวชาญของS<bool>::operator[] std::vector<bool>การเปลี่ยนชนิดส่งคืนเพื่อdecltype(auto)หลีกเลี่ยงปัญหานี้
Xoph
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.