C ++ 11 mendukung sintaks fungsi baru:
auto func_name(int x, int y) -> int;
Saat ini fungsi ini akan dideklarasikan sebagai:
int func_name(int x, int y);
Gaya baru tampaknya belum diadopsi secara luas (katakanlah di gcc stl)
Namun, haruskah gaya baru ini lebih disukai di mana pun di program C ++ 11 baru, atau apakah itu hanya akan digunakan saat diperlukan?
Secara pribadi, saya lebih suka gaya lama jika memungkinkan, tetapi basis kode dengan gaya campuran terlihat cukup jelek.
c++
c++11
auto
trailing-return-type
suram
sumber
sumber
decltype
argumen.Jawaban:
Ada kasus tertentu di mana Anda harus menggunakan jenis pengembalian jejak. Terutama, jenis pengembalian lambda, jika ditentukan, harus ditentukan melalui jenis pengembalian tambahan. Selain itu, jika tipe pengembalian Anda menggunakan
decltype
yang mengharuskan nama argumen berada dalam cakupan, tipe pengembalian jejak harus digunakan (namun, biasanya dapat digunakandeclval<T>
untuk mengatasi masalah yang terakhir ini).Jenis trailing return memang memiliki beberapa keuntungan kecil lainnya. Misalnya, pertimbangkan definisi fungsi anggota non-inline menggunakan sintaks fungsi tradisional:
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 { // ... }
Anggota typedefs tidak berada dalam lingkup sampai nama kelas muncul sebelumnya
::get_integers
, jadi kita harus mengulang kualifikasi kelas dua kali. Jika kita menggunakan tipe pengembalian jejak, kita tidak perlu mengulangi nama tipe:auto my_awesome_type::get_integers() const -> integer_sequence { // ... }
Dalam contoh ini, ini bukan masalah besar, tetapi jika Anda memiliki nama kelas yang panjang atau fungsi anggota template kelas yang tidak ditentukan secara inline, hal ini dapat membuat perbedaan besar dalam keterbacaan.
Dalam sesi "Fresh Paint" di C ++ Now 2012, Alisdair Meredith menunjukkan bahwa jika Anda menggunakan tipe pengembalian trailing secara konsisten, nama semua fungsi Anda berbaris rapi:
auto foo() -> int; auto bar() -> really_long_typedef_name;
Saya sudah digunakan mengikuti tipe kembali mana-mana di CxxReflect , jadi jika Anda sedang mencari contoh bagaimana kode terlihat menggunakan mereka secara konsisten, Anda dapat melihat ada (misalnya, yang
type
kelas ).sumber
Selain apa yang orang lain katakan, tipe kembali trailing juga memungkinkan untuk digunakan
this
, yang sebaliknya tidak diperbolehkanstruct 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(); } };
Dalam deklarasi kedua, kami menggunakan gaya tradisional. Namun karena
this
tidak diperbolehkan pada posisi itu, kompilator tidak menggunakannya secara implisit. Jadia.end()
penggunaan jenis yang dideklarasikan secara statisa
untuk menentukanend
kelebihan beban apavector<int>
yang akan dipanggil, yang akhirnya menjadi versi non-const.sumber
Keuntungan lainnya adalah sintaks tipe trailing-return bisa lebih mudah dibaca ketika fungsi mengembalikan pointer ke suatu fungsi. Misalnya, bandingkan
void (*get_func_on(int i))(int);
dengan
auto get_func_on(int i) -> void (*)(int);
Namun, orang dapat berargumen bahwa keterbacaan yang lebih baik dapat dicapai hanya dengan memperkenalkan alias tipe untuk penunjuk fungsi:
using FuncPtr = void (*)(int); FuncPtr get_func_on(int i);
sumber
Lihat artikel bagus ini: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html Contoh yang sangat bagus ketika menggunakan sintaks ini tanpa deklarasi dalam permainan :
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; }
Dan penjelasan brilian juga dicuri dari artikel Alex Allain "Karena nilai kembalian berada di akhir fungsi, bukan sebelumnya, Anda tidak perlu menambahkan cakupan kelas."
Bandingkan dengan kemungkinan kasus ini ketika seseorang secara tidak sengaja melupakan ruang lingkup kelas, dan, untuk bencana yang lebih besar, PersonType lain didefinisikan dalam cakupan global:
typedef float PersonType; // just for even more trouble /*missing: Person::*/ PersonType Person::getPersonType () { return _person_type; }
sumber
prog.cpp:13:12: error: prototype for 'PersonType Person::getPersonType()' does not match any in class 'Person'
vs.prog.cpp:13:1: error: 'PersonType' does not name a type
Kesalahan pertama dari kompilator adalah, setidaknya bagi saya, lebih buruk untuk dipahami.