รูปแบบไวยากรณ์ชนิดการส่งคืนต่อท้ายควรเป็นค่าเริ่มต้นสำหรับโปรแกรม C ++ 11 ใหม่หรือไม่ [ปิด]


92

C ++ 11 รองรับไวยากรณ์ของฟังก์ชันใหม่:

auto func_name(int x, int y) -> int;

ปัจจุบันฟังก์ชันนี้จะประกาศเป็น:

int func_name(int x, int y);

รูปแบบใหม่ดูเหมือนจะยังไม่ได้รับการยอมรับอย่างกว้างขวาง (พูดใน gcc stl)

อย่างไรก็ตามควรใช้สไตล์ใหม่นี้ทุกที่ในโปรแกรม C ++ 11 ใหม่หรือจะใช้เมื่อจำเป็นเท่านั้น?

โดยส่วนตัวแล้วฉันชอบรูปแบบเก่าเมื่อเป็นไปได้ แต่ฐานรหัสที่มีรูปแบบผสมดูค่อนข้างน่าเกลียด


29
ส่วนใหญ่มีไว้สำหรับการdecltypeโต้แย้ง
Cat Plus Plus

สิ่งที่ CatPlusPlus พูด: ไม่ค่อยสมเหตุสมผลเมื่อใช้ในตัวอย่างของคุณ
stijn

@Cat Plus Plus ซึ่งหมายความว่าคุณทิ้งสิ่งต่าง ๆ ตามที่อยู่ใน C ++ 03 เว้นแต่คุณจะต้องได้รับประเภทการส่งคืน?
mirk

1
น่าเกลียดที่ต้องระบุ "auto" ไว้หน้าทุกฟังก์ชัน นั่นเหมือนกับคำตอบที่มีชีวิตชีวาของ C ++ สำหรับ "def" ของ python หรือไม่?
Erik Aronesty

คำตอบ:


110

มีบางกรณีที่คุณต้องใช้ประเภทผลตอบแทนต่อท้าย โดยเฉพาะอย่างยิ่งประเภทการส่งคืนแลมบ์ดาหากระบุไว้จะต้องถูกระบุผ่านประเภทผลตอบแทนต่อท้าย นอกจากนี้หากประเภทการส่งคืนของคุณใช้ a decltypeที่กำหนดให้ชื่ออาร์กิวเมนต์อยู่ในขอบเขตจะต้องใช้ประเภทการส่งคืนต่อท้าย (อย่างไรก็ตามโดยปกติแล้วสามารถใช้declval<T>เพื่อแก้ไขปัญหาหลังนี้ได้)

ประเภทผลตอบแทนต่อท้ายมีข้อดีเล็กน้อยอื่น ๆ ตัวอย่างเช่นพิจารณานิยามฟังก์ชันสมาชิกที่ไม่ใช่อินไลน์โดยใช้ไวยากรณ์ของฟังก์ชันดั้งเดิม:

struct my_awesome_type
{
    typedef std::vector<int> integer_sequence;

    integer_sequence get_integers() const;
}; 

my_awesome_type::integer_sequence my_awesome_type::get_integers() const
{
    // ...
}

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

auto my_awesome_type::get_integers() const -> integer_sequence
{
    // ...
}

ในตัวอย่างนี้ไม่ใช่เรื่องใหญ่ แต่ถ้าคุณมีชื่อคลาสแบบยาวหรือฟังก์ชันสมาชิกของเทมเพลตคลาสที่ไม่ได้กำหนดแบบอินไลน์ก็สามารถสร้างความแตกต่างอย่างมากในการอ่านได้

ในเซสชัน"Fresh Paint"ที่ C ++ Now 2012 Alisdair Meredith ได้ชี้ให้เห็นว่าหากคุณใช้ประเภทการส่งคืนต่อท้ายอย่างสม่ำเสมอชื่อของฟังก์ชันทั้งหมดของคุณจะเรียงลำดับอย่างเรียบร้อย:

auto foo() -> int;
auto bar() -> really_long_typedef_name;

ผมเคยใช้ต่อท้ายประเภทผลตอบแทนทุกที่ในCxxReflectดังนั้นหากคุณกำลังมองหาตัวอย่างของวิธีการรหัสรูปลักษณ์โดยใช้พวกเขาอย่างต่อเนื่องคุณสามารถดูมี (เช่นชั้น )type


1
ดูเหมือนว่าจะยังไม่มีฉันทามติ แต่ก็น่าสนใจที่จะดู CxxReflect ด้วยรูปแบบใหม่
mirk

สวัสดีเจมส์ คำตอบนี้อาจทำให้ถูกต้องมากขึ้นในแง่ของมาตรฐาน C ++ 14
Drew Dormann

@DrewDormann คุณจะเพิ่ม / เปลี่ยนแปลงอะไร
underscore_d

การจัดตำแหน่งเป็นข้อดีอย่างมากจนถึงจุดที่ฉันต้องการให้มีคำหลัก 'func' ใหม่เพื่อแทนที่ 'อัตโนมัติ' ที่ไม่มีความหมายที่นี่
Johan Boulé

67

นอกจากสิ่งที่คนอื่นพูดแล้วประเภทผลตอบแทนต่อท้ายยังอนุญาตให้ใช้thisซึ่งไม่ได้รับอนุญาตเป็นอย่างอื่น

struct A {
  std::vector<int> a;

  // OK, works as expected
  auto begin() const -> decltype(a.begin()) { return a.begin(); }

  // FAIL, does not work: "decltype(a.end())" will be "iterator", but 
  // the return statement returns "const_iterator"
  decltype(a.end()) end() const { return a.end(); }
};

ในการประกาศครั้งที่สองเราใช้รูปแบบดั้งเดิม อย่างไรก็ตามเนื่องจากthisไม่ได้รับอนุญาตในตำแหน่งนั้นคอมไพเลอร์จะไม่ใช้มันโดยปริยาย ดังนั้นการa.end()ใช้ประเภทที่ประกาศแบบคงที่aเพื่อกำหนดว่าจะเรียกendใช้งานเกินพิกัดvector<int>ใดซึ่งจะกลายเป็นเวอร์ชันที่ไม่ใช่ const


2
แม้ว่านี่จะเป็นการสาธิตแนวคิดที่ดี / เรียบร้อย (โดยใช้สมาชิกในประเภทผลตอบแทน) แต่ก็ตลกดีเนื่องจากใน C ++ 14 การระบุประเภทซ้ำซ้อนโดยสิ้นเชิงในคำจำกัดความแบบอินไลน์โดยไม่มีการแปลง ตอนนี้เราสามารถใช้การหักประเภทผลตอบแทนเต็มจำนวน : P
underscore_d

27

ข้อดีอีกประการหนึ่งคือไวยากรณ์ชนิดต่อท้ายสามารถอ่านได้มากขึ้นเมื่อฟังก์ชันส่งคืนตัวชี้ไปยังฟังก์ชัน ตัวอย่างเช่นเปรียบเทียบ

void (*get_func_on(int i))(int);

ด้วย

auto get_func_on(int i) -> void (*)(int);

อย่างไรก็ตามเราสามารถโต้แย้งว่าความสามารถในการอ่านที่ดีขึ้นสามารถทำได้เพียงแค่แนะนำประเภทนามแฝงสำหรับตัวชี้ฟังก์ชัน:

using FuncPtr = void (*)(int);
FuncPtr get_func_on(int i);

10

ดูบทความที่ดีนี้: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html ตัวอย่างที่ดีมากเมื่อใช้ไวยากรณ์นี้โดยไม่มีประเภทในเกม :

class Person
{
public:
    enum PersonType { ADULT, CHILD, SENIOR };
    void setPersonType (PersonType person_type);
    PersonType getPersonType ();
private:
    PersonType _person_type;
};

auto Person::getPersonType () -> PersonType
{
    return _person_type;
}

และคำอธิบายที่ยอดเยี่ยมยังขโมยมาจากบทความของ Alex Allain "เนื่องจากค่าส่งคืนจะอยู่ที่ส่วนท้ายของฟังก์ชันแทนที่จะเป็นก่อนหน้านี้คุณจึงไม่จำเป็นต้องเพิ่มขอบเขตของคลาส"

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

typedef float PersonType; // just for even more trouble
/*missing: Person::*/
PersonType Person::getPersonType ()
{
    return _person_type;
}

7
ฉันไม่แน่ใจว่าสิ่งนี้จัดอยู่ในหมวดหมู่ "ภัยพิบัติ" หากพิมพ์ผิดรหัสจะไม่รวบรวม ข้อผิดพลาดรันไทม์อาจส่งผลร้ายแรง ข้อผิดพลาดในการรวบรวมเวลาไม่มากนัก
James McNellis

4
@JamesMcNellis เปรียบเทียบเอาต์พุตของคอมไพเลอร์: prog.cpp:13:12: error: prototype for 'PersonType Person::getPersonType()' does not match any in class 'Person'กับprog.cpp:13:1: error: 'PersonType' does not name a type ข้อผิดพลาดแรกจากคอมไพเลอร์คืออย่างน้อยสำหรับฉันก็แย่กว่าที่จะเข้าใจ
PiotrNycz

โดยส่วนตัวแล้วฉันไม่เห็นด้วยฉันพบว่าข้อความที่สองอ่านยากกว่าและฉันอยากให้การใช้งานดูเหมือนการประกาศ
ย.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.