Kapan menggunakan typedef?

14

Saya agak bingung tentang apakah dan kapan saya harus menggunakan typedef di C ++. Saya merasa ini adalah tindakan penyeimbang antara keterbacaan dan kejelasan.

Berikut ini contoh kode tanpa salah ketik:

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    static std::map<std::tuple<std::vector<int>::const_iterator,
                               std::vector<int>::const_iterator>,
                    int> lookup_table;

    std::map<std::tuple<std::vector<int>::const_iterator,
                        std::vector<int>::const_iterator>, int>::iterator lookup_it =
        lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

IMO sangat jelek. Jadi saya akan menambahkan beberapa typedefs di dalam fungsi agar terlihat lebih bagus:

int sum(std::vector<int>::const_iterator first, 
        std::vector<int>::const_iterator last)
{
    typedef std::tuple<std::vector<int>::const_iterator,
                       std::vector<int>::const_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

Kode ini masih agak canggung, tetapi saya menyingkirkan sebagian besar bahan mimpi buruk. Tapi masih ada iterator vektor int, varian ini menghilangkannya:

typedef std::vector<int>::const_iterator Input_iterator;

int sum(Input_iterator first, Input_iterator last)
{
    typedef std::tuple<Input_iterator, Input_iterator> Lookup_key;
    typedef std::map<Lookup_key, int> Lookup_table;

    static Lookup_table lookup_table;

    Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);

    if (lookup_it != lookup_table.end())
        return lookup_it->second;            

    ...
}

Ini terlihat bersih, tetapi apakah masih dapat dibaca?

Kapan saya harus menggunakan typedef? Begitu saya memiliki jenis mimpi buruk? Begitu itu terjadi lebih dari satu kali? Di mana saya harus meletakkannya? Haruskah saya menggunakannya dalam tanda tangan fungsi atau menyimpannya dalam implementasi?

futlib
sumber
1
Bukan duplikat, tetapi agak terkait dengan pertanyaan saya programmer.stackexchange.com/questions/130679/…
c0da
typedef Input_iterator std::vector<int>::const_iterator;mundur
Per Johansson
1
Ada perbedaan antara keterbacaan dan kejelasan?
Neil
Ketika #definetidak cukup baik.
Thomas Eding

Jawaban:

6

Contoh terakhir Anda sangat mudah dibaca, tetapi tergantung di mana Anda mendefinisikan typedef. Typedef lingkup lokal (seperti dalam contoh kedua Anda) adalah IMVHO hampir selalu menang.

Saya masih suka contoh ketiga Anda yang terbaik, tetapi Anda mungkin ingin memikirkan penamaannya, dan memberikan nama iterator yang memberi tahu maksud dari wadah tersebut.

Pilihan lain adalah membuat templat dari fungsi Anda, sehingga berfungsi dengan wadah yang berbeda juga. Sepanjang garis

template <typename Input_iterator> ... sum(Input_iterator first, Input_iterator last) 

yang juga sangat dalam semangat STL.

Fabio Fracassi
sumber
2

Anggap typedefsebagai deklarasi variabel yang setara dengan fungsi: itu ada di sana sehingga Anda ...

  • ... tidak perlu mengulangi diri Anda sendiri ketika menggunakan kembali jenis yang sama (yang dilakukan dua contoh pertama Anda).
  • ... dapat menyembunyikan detail berdarah dari tipe sehingga tidak selalu ditampilkan.
  • ... pastikan bahwa perubahan pada jenis dicerminkan di setiap tempat yang mereka gunakan.

Secara pribadi, saya senang jika saya harus membaca nama yang panjang seperti std::vector<int>::const_iteratorberulang kali.

Contoh ketiga Anda tidak perlu berulang dengan sendirinya dan lebih mudah dibaca.

Blrfl
sumber
1

typedefdeklarasi pada dasarnya melayani tujuan yang sama dengan enkapsulasi. Karena alasan itu, mereka hampir selalu paling cocok di file header, mengikuti konvensi penamaan yang sama dengan kelas Anda, karena:

  • Jika Anda memerlukan typedef, kemungkinan penelepon juga akan, terutama seperti dalam contoh Anda di mana ia digunakan dalam argumen.
  • Jika Anda perlu mengubah jenis untuk alasan apa pun, termasuk menggantinya dengan kelas Anda sendiri, Anda hanya perlu melakukannya di satu tempat.
  • Itu membuat Anda kurang rentan terhadap kesalahan karena berulang kali menulis jenis yang kompleks.
  • Ini menyembunyikan detail implementasi yang tidak perlu.

Selain itu, kode memoisasi Anda akan jauh lebih bersih jika Anda abstrakkan lebih lanjut, seperti:

if (lookup_table.exists(first, last))
    return lookup_table.get(first, last);
Karl Bielefeldt
sumber
Saran Anda mungkin terlihat lebih bersih, tetapi membuang waktu dengan melakukan pencarian dua kali.
Derek Ledbetter
Yap, itu trade off yang disengaja. Namun, ada beberapa cara untuk melakukannya dengan pencarian tunggal yang hampir sama bersihnya, terutama jika Anda tidak khawatir tentang keamanan benang.
Karl Bielefeldt