Apa perbedaan antara 'typedef' dan 'using' di C ++ 11?

905

Saya tahu bahwa di C ++ 11 sekarang kita dapat menggunakan usinguntuk menulis tipe alias, seperti typedefs:

typedef int MyInt;

Apakah, dari apa yang saya mengerti, setara dengan:

using MyInt = int;

Dan sintaks baru itu muncul dari upaya untuk memiliki cara untuk mengekspresikan " template typedef":

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

Tetapi, dengan dua contoh non-templat pertama, apakah ada perbedaan halus lainnya dalam standar? Misalnya, typedeflakukan aliasing dengan cara "lemah". Itu tidak membuat jenis baru tetapi hanya nama baru (konversi tersirat di antara nama-nama).

Apakah sama dengan usingatau apakah itu menghasilkan tipe baru? Apakah ada perbedaan?

Klaim
sumber
215
Saya pribadi lebih suka sintaks baru karena jauh lebih mirip dengan tugas variabel biasa, meningkatkan keterbacaan. Misalnya, apakah Anda lebih suka typedef void (&MyFunc)(int,int);atau tidak using MyFunc = void(int,int);?
Matthieu M.
13
Saya sepenuhnya setuju, saya hanya menggunakan sintaks baru sekarang. Itu sebabnya saya bertanya, untuk memastikan tidak ada perbedaan.
Klaim
80
@ MatthieuM. keduanya berbeda btw. Seharusnya typedef void MyFunc(int,int);(yang sebenarnya tidak terlihat buruk), atauusing MyFunc = void(&)(int,int);
R. Martinho Fernandes
5
@ R.MartinhoFernandes mengapa Anda perlu (&) di using MyFunc = void(&)(int,int);? Apakah maksudnya MyFuncadalah referensi ke suatu fungsi? bagaimana jika Anda menghilangkan & ?
Kaya
7
Ya, itu adalah referensi fungsi. Ini setara dengan typedef void (&MyFunc)(int,int);. Jika Anda menghilangkan &itu setara dengantypedef void MyFunc(int,int);
R. Martinho Fernandes

Jawaban:

585

Mereka setara, dari standar (tambang penekanan) (7.1.3.2):

Nama typedef juga dapat diperkenalkan dengan deklarasi alias. Identifier yang mengikuti kata kunci yang digunakan menjadi nama typedef dan atribut-specifier-seq opsional yang mengikuti identifier sesuai dengan nama typedef itu. Ini memiliki semantik yang sama seolah-olah diperkenalkan oleh specedef specifier. Secara khusus, itu tidak mendefinisikan tipe baru dan itu tidak akan muncul di type-id.

Jesse Bagus
sumber
24
Dari jawabannya, usingkata kunci tampaknya menjadi superset dari typedef. Lalu, apakah typdefakan ditinggalkan di masa depan?
iammilind
49
Penghentian tidak selalu menunjukkan niat untuk menghapus - Itu hanya berdiri sebagai rekomendasi yang sangat kuat untuk memilih cara lain.
Iron Savior
18
Tapi kemudian saya bertanya-tanya mengapa mereka tidak hanya mengizinkan typedef untuk digunakan. Saya ingat pernah membaca di suatu tempat bahwa mereka memperkenalkan usingsintaksis tepatnya karena typedefsemantik tidak bekerja dengan baik dengan template. Yang entah bagaimana bertentangan dengan fakta yang usingdidefinisikan memiliki semantik yang persis sama.
celtschk
12
@celtschk: Alasannya dibicarakan dalam proposal n1489. Alias ​​templat bukan alias untuk jenis, tetapi alias untuk sekelompok templat. Untuk membuat perbedaan antara typedefyang dirasa perlu sintaksis baru. Juga, perlu diingat pertanyaan OP adalah tentang perbedaan antara versi non-template.
Jesse Good
4
Jadi mengapa redundansi ini diperkenalkan? 2 sintaks untuk tujuan yang sama. Dan saya tidak typdefpernah merasa ditinggalkan.
Benoît
237

Mereka sebagian besar sama, kecuali bahwa:

Deklarasi alias kompatibel dengan template, sedangkan typedef gaya C tidak.

Zhongming Qu
sumber
29
Terutama menyukai kesederhanaan jawaban dan menunjukkan asal-usul pengaturan huruf.
g24l
1
@ g24l maksudmu typedef ... mungkin
Marc.2377
Apa perbedaan antara C dan C ++ typedefjika saya dapat bertanya?
McSinyx
196

The menggunakan sintaks memiliki keuntungan ketika digunakan dalam template. Jika Anda memerlukan abstraksi jenis, tetapi juga perlu menjaga parameter templat agar dapat ditentukan di masa mendatang. Anda harus menulis sesuatu seperti ini.

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; }

Tetapi menggunakan sintaksis menyederhanakan use case ini.

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

my_type<int> variable;
template <typename U> struct baz { my_type<U> _var_member; }
4xy
sumber
35
Saya sudah menunjukkan ini dalam pertanyaan. Pertanyaan saya adalah tentang apakah Anda tidak menggunakan template apakah ada perbedaan dengan typedef. Misalnya, ketika Anda menggunakan 'Foo foo {init_value};' alih-alih 'Foo foo (init_value)' keduanya seharusnya melakukan hal yang sama, tetapi jangan foillow persis aturan yang sama. Jadi saya bertanya-tanya apakah ada perbedaan tersembunyi yang serupa dengan menggunakan / typedef.
Klaim
24

Mereka pada dasarnya sama tetapi usingmenyediakan alias templatesyang cukup berguna. Satu contoh bagus yang bisa saya temukan adalah sebagai berikut:

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

Jadi, kita bisa menggunakan std::add_const_t<T>bukannyatypename std::add_const<T>::type

Validus Oculus
sumber
Sejauh yang saya tahu, itu adalah perilaku yang tidak terdefinisi untuk menambahkan sesuatu di dalam namespace std
someonewithpc
5
@someonewithpc Saya tidak menambahkan apa-apa, sudah ada, saya baru saja menunjukkan contoh penggunaan nama ketik. Silakan periksa en.cppreference.com/w/cpp/types/add_cv
Validus Oculus
9

Saya tahu poster asli memiliki jawaban yang bagus, tetapi bagi siapa pun yang tersandung pada utas ini seperti yang saya miliki ada catatan penting dari proposal yang menurut saya menambah sesuatu yang bernilai untuk diskusi di sini, terutama untuk keprihatinan dalam komentar tentang apakah typedefkata kunci tersebut akan ditandai sebagai usang di masa mendatang, atau dihapus karena menjadi redundan / lama:

Telah disarankan untuk (kembali) menggunakan kata kunci typedef ... untuk memperkenalkan alias templat:

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

Notasi itu memiliki keuntungan menggunakan kata kunci yang sudah dikenal untuk memperkenalkan jenis alias. Namun, ia juga menampilkan beberapa ketidaksenangan [sic] di antaranya kebingungan menggunakan kata kunci yang dikenal untuk memperkenalkan alias untuk nama-jenis dalam konteks di mana alias tidak menunjuk suatu jenis, tetapi sebuah templat; Vecadalah tidak alias untuk jenis, dan tidak harus diambil untuk typedef-nama. Nama Vecadalah nama untuk keluarga std::vector<•, MyAllocator<•> >- di mana peluru adalah pengganti untuk tipe-nama. Oleh karena itu kami tidak mengusulkan sintaks "typedef". Di sisi lain kalimat

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

dapat dibaca / ditafsirkan sebagai: mulai sekarang, saya akan menggunakan Vec<T>sinonim untukstd::vector<T, MyAllocator<T> > . Dengan pembacaan itu, sintaks baru untuk aliasing tampaknya cukup logis.

Bagi saya, ini menyiratkan dukungan berkelanjutan untuk typedefkata kunci dalam C ++ karena masih dapat membuat kode lebih mudah dibaca dan dimengerti .

Memperbarui usingkata kunci khusus untuk templat, dan (seperti yang ditunjukkan dalam jawaban yang diterima) ketika Anda bekerja dengan non-templat usingdan typedefidentik secara mekanis, sehingga pilihannya sepenuhnya tergantung pada programmer dengan alasan keterbacaan dan komunikasi maksud. .

RoboticForest
sumber
2

Kedua kata kunci itu setara, tetapi ada beberapa peringatan. Salah satunya adalah bahwa mendeklarasikan pointer fungsi dengan using T = int (*)(int, int);lebih jelas daripada dengan typedef int (*T)(int, int);. Kedua adalah bahwa bentuk alias template tidak mungkin dengan typedef. Ketiga adalah bahwa mengekspos C API akan membutuhkan typedefdi header publik.

marski
sumber
0

Deklarasi typedef dapat, sedangkan deklarasi alias tidak bisa, digunakan sebagai pernyataan inisialisasi

Tetapi, dengan dua contoh non-templat pertama, apakah ada perbedaan halus lainnya dalam standar?

Meskipun kasus sudut, deklarasi typedef adalah init-statement dan dengan demikian dapat digunakan dalam konteks yang memungkinkan pernyataan inisialisasi

// 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

sedangkan alias-deklarasi adalah bukan sebuah init-pernyataan , dan mungkin dengan demikian tidak digunakan dalam konteks yang memungkinkan pernyataan inisialisasi

// 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
dfri
sumber