อะไรคือความแตกต่างระหว่าง 'typedef' และ 'using' ใน C ++ 11?


905

ฉันรู้ว่าใน C ++ 11 ตอนนี้เราสามารถใช้usingเพื่อเขียนนามแฝงประเภทเช่นtypedefs:

typedef int MyInt;

คือจากสิ่งที่ฉันเข้าใจเทียบเท่ากับ:

using MyInt = int;

และไวยากรณ์ใหม่นั้นเกิดขึ้นจากความพยายามที่จะมีวิธีในการแสดงออก " template typedef":

template< class T > using MyType = AnotherType< T, MyAllocatorType >;

แต่ด้วยสองตัวอย่างแรกที่ไม่ใช่แม่แบบมีความแตกต่างเล็กน้อยอื่น ๆ ในมาตรฐานหรือไม่ ตัวอย่างเช่นใช้typedefนามแฝงด้วยวิธี "อ่อนแอ" นั่นคือมันไม่ได้สร้างประเภทใหม่ แต่มีเพียงชื่อใหม่เท่านั้น (การแปลงนั้นมีนัยยะระหว่างชื่อเหล่านั้น)

มันเหมือนกันกับusingหรือสร้างชนิดใหม่หรือไม่ มีความแตกต่างหรือไม่?


215
ฉันชอบไวยากรณ์ใหม่เป็นการส่วนตัวมากกว่าเพราะมันคล้ายกับการกำหนดตัวแปรปกติมากขึ้นทำให้อ่านง่ายขึ้น ตัวอย่างเช่นคุณชอบtypedef void (&MyFunc)(int,int);หรือusing MyFunc = void(int,int);?
Matthieu M.

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

80
@MatthieuM สองคนนั้นต่างกัน btw ควรเป็นtypedef void MyFunc(int,int);(ซึ่งจริงๆแล้วดูไม่เลว) หรือusing MyFunc = void(&)(int,int);
R. Martinho Fernandes

5
@ R.MartinhoFernandes ทำไมคุณต้อง(&)ใน using MyFunc = void(&)(int,int);? หมายความว่าMyFuncเป็นการอ้างอิงถึงฟังก์ชั่นหรือไม่? สิ่งที่ถ้าคุณละเว้น& ?
รวย

7
ใช่มันเป็นการอ้างอิงฟังก์ชั่น typedef void (&MyFunc)(int,int);มันเทียบเท่ากับ หากคุณละเว้นสิ่ง&นี้ก็เท่ากับtypedef void MyFunc(int,int);
R. Martinho Fernandes

คำตอบ:


585

พวกเขาจะเทียบเท่าจากมาตรฐาน (เหมืองเน้น) (7.1.3.2):

ชื่อ typedef สามารถนำมาใช้โดย alias-declaration ตัวระบุที่อยู่ต่อจากคำสำคัญในการใช้จะกลายเป็น typedef-name และ attribute-specifier-seq ที่เป็นตัวเลือกหลังจากตัวระบุที่เกี่ยวข้องกับ typedef-name มันมีความหมายเช่นเดียวกับถ้ามันได้รับการแนะนำให้รู้จักกับตัวระบุ typedef โดยเฉพาะอย่างยิ่งมันไม่ได้กำหนดประเภทใหม่และมันจะไม่ปรากฏในประเภท ID


24
จากคำตอบของคำหลักที่น่าจะเป็นซูเปอร์using typedefจากนั้นจะtypdefถูกยกเลิกในอนาคตหรือไม่
iammilind

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

18
แต่ฉันสงสัยว่าทำไมพวกเขาไม่อนุญาตให้พิมพ์เทมเพลท ฉันจำได้ว่าต้องอ่านที่ไหนสักแห่งที่พวกเขาแนะนำusingไวยากรณ์อย่างแม่นยำเพราะtypedefความหมายนั้นใช้งานไม่ได้กับเทมเพลต ซึ่งขัดแย้งกับความจริงที่usingกำหนดให้มีความหมายเหมือนกัน
celtschk

12
@celtschk: n1489เหตุผลที่จะพูดคุยเกี่ยวกับข้อเสนอใน เทมเพลตนามแฝงไม่ใช่นามแฝงสำหรับประเภท แต่เป็นนามแฝงสำหรับกลุ่มของเทมเพลต เพื่อสร้างความแตกต่างระหว่างtypedefความรู้สึกที่ต้องการสำหรับไวยากรณ์ใหม่ นอกจากนี้โปรดทราบว่าคำถามของ OP เกี่ยวกับความแตกต่างระหว่างเวอร์ชันที่ไม่ใช่เทมเพลต
Jesse Good

4
เหตุใดจึงมีความซ้ำซ้อนนี้แนะนำ? 2 ไวยากรณ์สำหรับวัตถุประสงค์เดียวกัน และฉันไม่เห็นtypdefว่าเลิกใช้แล้ว
Benoît

237

พวกเขาส่วนใหญ่เหมือนกันยกเว้นว่า:

การประกาศนามแฝงเข้ากันได้กับแม่แบบในขณะที่สไตล์ C typedef ไม่ได้


29
โดยเฉพาะอย่างยิ่งชอบความเรียบง่ายของคำตอบและชี้ให้เห็นที่มาของเรียงพิมพ์
g24l

1
@ g24l คุณหมายถึง typedef ... อาจจะ
Marc.2377

อะไรคือความแตกต่างระหว่าง C และ C ++ typedefหากฉันถาม
McSinyx

196

ใช้ไวยากรณ์มีความได้เปรียบเมื่อใช้ในแม่แบบ หากคุณต้องการนามธรรมประเภท แต่ยังต้องให้พารามิเตอร์แม่แบบที่จะเป็นไปได้ที่จะระบุในอนาคต คุณควรเขียนอะไรแบบนี้

template <typename T> struct whatever {};

template <typename T> struct rebind
{
  typedef whatever<T> type; // to make it possible to substitue the whatever in future.
};

rebind<int>::type variable;

template <typename U> struct bar { typename rebind<U>::type _var_member; }

แต่การใช้ไวยากรณ์ช่วยให้การใช้งานง่ายขึ้น

template <typename T> using my_type = whatever<T>;

my_type<int> variable;
template <typename U> struct baz { my_type<U> _var_member; }

35
ฉันชี้ไปที่คำถามนี้แล้ว คำถามของฉันเกี่ยวกับถ้าคุณไม่ใช้แม่แบบมีความแตกต่างกับ typedef ตัวอย่างเช่นเมื่อคุณใช้ 'Foo foo {init_value};' แทนที่จะเป็น 'Foo foo (init_value)' ทั้งคู่ควรทำสิ่งเดียวกัน แต่อย่าทำตามกฎเดียวกัน ดังนั้นฉันจึงสงสัยว่ามีความแตกต่างที่คล้ายกันกับการใช้ / typedef หรือไม่
Klaim

24

พวกเขาเป็นหลักเหมือนกัน แต่usingให้alias templatesซึ่งมีประโยชน์มาก ตัวอย่างที่ดีหนึ่งที่ฉันสามารถหาได้มีดังนี้:

namespace std {
 template<typename T> using add_const_t = typename add_const<T>::type;
}

ดังนั้นเราสามารถใช้std::add_const_t<T>แทนtypename std::add_const<T>::type


เท่าที่ฉันรู้มันเป็นพฤติกรรมที่ไม่ได้กำหนดเพื่อเพิ่มอะไรใน std namespace
someonewithpc

5
@someonewithpc ฉันไม่ได้เพิ่มอะไรเลยมันมีอยู่แล้วฉันแค่แสดงตัวอย่างของการใช้ชื่อพิมพ์ โปรดตรวจสอบen.cppreference.com/w/cpp/types/add_cv
Validus Oculus

9

ฉันรู้ว่าผู้โพสต์ดั้งเดิมมีคำตอบที่ดี แต่สำหรับทุกคนที่สะดุดในหัวข้อนี้เช่นฉันมีข้อความสำคัญจากข้อเสนอที่ฉันคิดว่าจะเพิ่มคุณค่าบางอย่างให้กับการสนทนาที่นี่โดยเฉพาะความกังวลในความคิดเห็นเกี่ยวกับtypedefคำหลัก จะถูกทำเครื่องหมายว่าเลิกใช้แล้วในอนาคตหรือถูกลบเนื่องจากการซ้ำซ้อน / เก่า:

มีการแนะนำให้ (อีกครั้ง) ใช้คำหลัก typedef ... เพื่อแนะนำชื่อแทนเทมเพลต:

template<class T>
  typedef std::vector<T, MyAllocator<T> > Vec;

สัญกรณ์ดังกล่าวมีข้อได้เปรียบในการใช้คำหลักที่รู้จักกันแล้วเพื่อแนะนำนามแฝงประเภท อย่างไรก็ตามมันยังแสดงข้อเสียหลายประการ [sic] ซึ่งความสับสนของการใช้คำสำคัญที่รู้จักกันในการแนะนำนามแฝงสำหรับชื่อประเภทในบริบทที่นามแฝงไม่ได้กำหนดประเภท แต่แม่แบบ; Vecคือไม่นามแฝงสำหรับประเภทและไม่ควรนำมาเป็น typedef ชื่อ ชื่อVecเป็นชื่อของตระกูลstd::vector<•, MyAllocator<•> >- โดยที่สัญลักษณ์แสดงหัวข้อย่อยเป็นตัวยึดสำหรับชื่อประเภทดังนั้นเราจึงไม่เสนอไวยากรณ์ "typedef" ในทางกลับกันประโยค

template<class T>
  using Vec = std::vector<T, MyAllocator<T> >;

สามารถอ่าน / ตีความว่าเป็น: จากนี้ไปผมจะใช้Vec<T>std::vector<T, MyAllocator<T> >เป็นคำพ้องสำหรับ ด้วยการอ่านนั้นไวยากรณ์ใหม่สำหรับนามแฝงดูเหมือนว่าสมเหตุสมผล

สำหรับฉันแล้วสิ่งนี้แสดงถึงการสนับสนุนtypedefคำหลักใน C ++ อย่างต่อเนื่องเพราะมันสามารถทำให้โค้ดอ่านและเข้าใจได้ง่ายขึ้น

การอัปเดตusingคำหลักนั้นใช้สำหรับเทมเพลตโดยเฉพาะและ (ตามที่อธิบายไว้ในคำตอบที่ยอมรับได้) เมื่อคุณทำงานกับที่ไม่ใช่เท็มเพลตusingและtypedefมีกลไกเหมือนกันดังนั้นตัวเลือกจะขึ้นอยู่กับโปรแกรมเมอร์โดยสมบูรณ์ .


2

คำหลักทั้งสองมีความเทียบเท่า แต่มีคำเตือนไม่กี่คำ หนึ่งคือการที่ประกาศตัวชี้ฟังก์ชั่นเป็นที่ชัดเจนกว่าด้วยusing T = int (*)(int, int); ประการที่สองคือการที่แม่แบบฟอร์มนามแฝงเป็นไปไม่ได้ด้วยtypedef int (*T)(int, int); typedefประการที่สามคือการเปิดเผย C API จะต้องtypedefอยู่ในส่วนหัวสาธารณะ


0

การประกาศ Typedef สามารถในขณะที่ไม่สามารถใช้ alias declarations ได้

แต่ด้วยสองตัวอย่างแรกที่ไม่ใช่แม่แบบมีความแตกต่างเล็กน้อยอื่น ๆ ในมาตรฐานหรือไม่

แม้ว่าจะเป็นมุมเล็ก ๆ การประกาศ typedefเป็นคำสั่ง initและอาจถูกนำมาใช้ในบริบทที่อนุญาตให้เริ่มต้นงบ

// C++11 (C++03) (init. statement in for loop iteration statements).
for(typedef int Foo; Foo{} != 0;) {}

// C++17 (if and switch initialization statements).
if (typedef int Foo; true) { (void)Foo{}; }
//  ^^^^^^^^^^^^^^^ init-statement

switch(typedef int Foo; 0) { case 0: (void)Foo{}; }
//     ^^^^^^^^^^^^^^^ init-statement

// C++20 (range-based for loop initialization statements).
std::vector<int> v{1, 2, 3};
for(typedef int Foo; Foo f : v) { (void)f; }
//  ^^^^^^^^^^^^^^^ init-statement

ขณะที่นามแฝงประกาศเป็นไม่init คำสั่งและอาจทำให้ไม่ได้นำมาใช้ในบริบทซึ่งจะช่วยให้งบเริ่มต้น

// C++ 11.
for(using Foo = int; Foo{} != 0;) {}
//  ^^^^^^^^^^^^^^^ error: expected expression

// C++17 (initialization expressions in switch and if statements).
if (using Foo = int; true) { (void)Foo{}; }
//  ^^^^^^^^^^^^^^^ error: expected expression

switch(using Foo = int; 0) { case 0: (void)Foo{}; }
//     ^^^^^^^^^^^^^^^ error: expected expression

// C++20 (range-based for loop initialization statements).
std::vector<int> v{1, 2, 3};
for(using Foo = int; Foo f : v) { (void)f; }
//  ^^^^^^^^^^^^^^^ error: expected expression
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.