it-swarm-id.com

Fitur Tersembunyi C ++?

Tidak C++ cinta ketika datang ke "fitur tersembunyi" garis pertanyaan? Kupikir aku akan membuangnya di sana. Apa sajakah fitur tersembunyi dari C++?

114
Craig H

Sebagian besar programmer C++ terbiasa dengan operator ternary:

x = (y < 0) ? 10 : 20;

Namun, mereka tidak menyadari bahwa itu dapat digunakan sebagai nilai:

(a == 0 ? a : b) = 1;

yang merupakan singkatan

if (a == 0)
    a = 1;
else
    b = 1;

Gunakan dengan hati-hati :-)

308
Ferruccio

Anda dapat menempatkan URI ke dalam sumber C++ tanpa kesalahan. Sebagai contoh:

void foo() {
    http://stackoverflow.com/
    int bar = 4;

    ...
}
238
Ben

Pointer aritmatika.

Pemrogram C++ lebih suka menghindari pointer karena bug yang dapat dikenalkan.

C++ paling keren yang pernah saya lihat? Literal analog.

140
Anonymouse

Saya setuju dengan sebagian besar tulisan di sana: C++ adalah bahasa multi-paradigma, jadi fitur "tersembunyi" yang akan Anda temukan (selain "perilaku tidak terdefinisi" yang harus Anda hindari dengan cara apa pun) adalah penggunaan fasilitas yang cerdas.

Sebagian besar fasilitas itu bukan fitur bawaan bahasa, tetapi yang berbasis perpustakaan.

Yang paling penting adalah [~ # ~] raii [~ # ~] , sering diabaikan selama bertahun-tahun oleh pengembang C++ yang datang dari dunia C. Overloading operator seringkali merupakan fitur yang disalahpahami yang memungkinkan perilaku seperti array (operator subskrip), pointer seperti operasi (pointer pintar) dan build-in-like operasi (mengalikan matriks.

Penggunaan pengecualian seringkali sulit, tetapi dengan beberapa pekerjaan, dapat menghasilkan kode yang benar-benar kuat melalui pengecualian keamanan spesifikasi (termasuk kode yang tidak akan gagal, atau yang akan memiliki fitur seperti komit yang akan berhasil, atau kembali ke keadaan semula).

Fitur "tersembunyi" yang paling terkenal dari C++ adalah metaprogramming template , karena memungkinkan Anda menjalankan program Anda sebagian (atau seluruhnya) dieksekusi pada waktu kompilasi bukannya runtime. Ini sulit, meskipun, dan Anda harus memiliki pemahaman yang kuat pada template sebelum mencobanya.

Lain menggunakan paradigma berganda untuk menghasilkan "cara pemrograman" di luar leluhur C++, yaitu, C.

Dengan menggunakan functors , Anda dapat mensimulasikan fungsi, dengan keamanan jenis tambahan dan menjadi stateful. Menggunakan pola perintah , Anda dapat menunda eksekusi kode. Kebanyakan pola desain lainnya dapat dengan mudah dan efisien diimplementasikan dalam C++ untuk menghasilkan gaya pengkodean alternatif yang tidak seharusnya ada di dalam daftar "paradigma C++ resmi".

Dengan menggunakan templat , Anda dapat menghasilkan kode yang akan berfungsi pada sebagian besar jenis, termasuk bukan yang Anda pikirkan pada awalnya. Anda juga dapat meningkatkan keamanan tipe (seperti malloc safe/realloc/gratis yang aman). Fitur objek C++ benar-benar kuat (dan karenanya, berbahaya jika digunakan secara sembrono), tetapi bahkan polimorfisme dinamis memiliki versi statisnya dalam C++: the [~ # ~] crtp [~ # ~] .

Saya telah menemukan bahwa kebanyakan " C++ Efektif" - ketik buku dari Scott Meyers atau " C++ Luar Biasa" - jenis buku dari Herb Sutter agar mudah dibaca , dan cukup banyak info tentang fitur C++ yang dikenal dan kurang dikenal.

Di antara saya lebih disukai adalah salah satu yang harus membuat rambut setiap Java programmer bangkit dari horor: Dalam C++, cara yang paling berorientasi objek untuk menambahkan fitur ke sebuah objek melalui fungsi non-anggota bukan-teman, alih-alih fungsi-anggota (yaitu metode kelas), karena:

  • Dalam C++, antarmuka kelas adalah fungsi anggota dan fungsi non anggota di namespace yang sama

  • fungsi non-teman non-anggota tidak memiliki akses istimewa ke internal kelas. Dengan demikian, menggunakan fungsi anggota di atas bukan teman yang bukan anggota akan melemahkan enkapsulasi kelas.

Ini tidak pernah gagal mengejutkan pengembang yang berpengalaman sekalipun.

(Sumber: Antara lain, Guru Sutradara Ramuan online # 84: http://www.gotw.ca/gotw/084.htm )

119
paercebal

Salah satu fitur bahasa yang saya anggap agak tersembunyi, karena saya belum pernah mendengarnya sepanjang waktu di sekolah, adalah namespace alias. Itu tidak menarik perhatian saya sampai saya melihat contoh di dokumentasi dorongan. Tentu saja, sekarang saya tahu tentang itu Anda dapat menemukannya di referensi C++ standar.

namespace fs = boost::filesystem;

fs::path myPath( strPath, fs::native );
118
Jason Mock

Variabel tidak hanya dapat dideklarasikan di bagian init dari loop for, tetapi juga kelas dan fungsi.

for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) {
    ...
}

Itu memungkinkan banyak variabel dari tipe yang berbeda.

102

Operator array bersifat asosiatif.

A [8] adalah sinonim untuk * (A + 8). Karena penjumlahan adalah asosiatif, yang dapat ditulis ulang sebagai * (8 + A), yang merupakan sinonim untuk ..... 8 [A]

Anda tidak mengatakan berguna ... :-)

77
Colin Jensen

Satu hal yang sedikit diketahui adalah bahwa serikat pekerja juga bisa menjadi templat:

template<typename From, typename To>
union union_cast {
    From from;
    To   to;

    union_cast(From from)
        :from(from) { }

    To getTo() const { return to; }
};

Dan mereka dapat memiliki fungsi konstruktor dan anggota juga. Hanya tidak ada hubungannya dengan warisan (termasuk fungsi virtual).

73

C++ adalah standar, seharusnya tidak ada fitur tersembunyi ...

C++ adalah bahasa multi-paradigma, Anda bisa bertaruh uang terakhir Anda ada fitur tersembunyi. Salah satu contoh dari banyak: template metaprogramming . Tidak ada seorang pun di komite standar yang bermaksud bahwa ada sub-bahasa Turing-lengkap yang dijalankan pada waktu kompilasi.

72
Konrad Rudolph

Fitur tersembunyi lainnya yang tidak berfungsi di C adalah fungsi unary + operator. Anda dapat menggunakannya untuk mempromosikan dan meluruhkan segala hal

Mengonversi Enumerasi ke bilangan bulat

+AnEnumeratorValue

Dan nilai enumerator Anda yang sebelumnya memiliki tipe enumerasi sekarang memiliki tipe integer sempurna yang dapat ditampung nilainya. Secara manual, Anda tidak akan tahu tipe itu! Ini diperlukan misalnya ketika Anda ingin menerapkan operator kelebihan beban untuk penghitungan Anda.

Dapatkan nilai dari variabel

Anda harus menggunakan kelas yang menggunakan inisialisasi statis di dalam kelas tanpa definisi di luar kelas, tetapi kadang-kadang gagal menghubungkan? Operator dapat membantu membuat temporer tanpa membuat asumsi atau dependensi pada jenisnya

struct Foo {
  static int const value = 42;
};

// This does something interesting...
template<typename T>
void f(T const&);

int main() {
  // fails to link - tries to get the address of "Foo::value"!
  f(Foo::value);

  // works - pass a temporary value
  f(+Foo::value);
}

Membusuk array ke sebuah pointer

Apakah Anda ingin meneruskan dua petunjuk ke suatu fungsi, tetapi itu tidak akan berhasil? Operator dapat membantu

// This does something interesting...
template<typename T>
void f(T const& a, T const& b);

int main() {
  int a[2];
  int b[3];
  f(a, b); // won't work! different values for "T"!
  f(+a, +b); // works! T is "int*" both time
}

Seumur hidup sementara terikat dengan referensi const adalah satu yang sedikit orang tahu tentang. Atau setidaknya itu adalah pengetahuan C++ favorit saya yang tidak diketahui oleh kebanyakan orang.

const MyClass& x = MyClass(); // temporary exists as long as x is in scope
61
MSN

Fitur bagus yang tidak sering digunakan adalah blok try-catch fungsi-lebar:

int Function()
try
{
   // do something here
   return 42;
}
catch(...)
{
   return -1;
}

Penggunaan utama adalah menerjemahkan pengecualian ke kelas pengecualian lain dan rethrow, atau menerjemahkan antara pengecualian dan penanganan kode kesalahan berbasis-kembali.

52
vividos

Banyak yang tahu metafungsi identity/id, tetapi ada usecase yang bagus untuk kasus non-templat: Kemudahan penulisan deklarasi:

// void (*f)(); // same
id<void()>::type *f;

// void (*f(void(*p)()))(int); // same
id<void(int)>::type *f(id<void()>::type *p);

// int (*p)[2] = new int[10][2]; // same
id<int[2]>::type *p = new int[10][2];

// void (C::*p)(int) = 0; // same
id<void(int)>::type C::*p = 0;

Sangat membantu mendekripsi deklarasi C++!

// boost::identity is pretty much the same
template<typename T> 
struct id { typedef T type; };
44

Fitur yang cukup tersembunyi adalah Anda dapat mendefinisikan variabel di dalam kondisi if, dan cakupannya akan menjangkau hanya di atas if, dan blok-blok lainnya:

if(int * p = getPointer()) {
    // do something
}

Beberapa makro menggunakannya, misalnya untuk menyediakan beberapa lingkup "terkunci" seperti ini:

struct MutexLocker { 
    MutexLocker(Mutex&);
    ~MutexLocker(); 
    operator bool() const { return false; } 
private:
    Mutex &m;
};

#define locked(mutex) if(MutexLocker const& lock = MutexLocker(mutex)) {} else 

void someCriticalPath() {
    locked(myLocker) { /* ... */ }
}

BOOST_FOREACH juga menggunakannya di bawah tenda. Untuk menyelesaikan ini, itu tidak hanya mungkin di jika, tetapi juga di saklar:

switch(int value = getIt()) {
    // ...
}

dan dalam beberapa saat:

while(SomeThing t = getSomeThing()) {
    // ...
}

(dan juga dalam kondisi). Tapi saya tidak terlalu yakin apakah semua ini berguna :)

43

Mencegah operator koma dari memanggil kelebihan operator

Kadang-kadang Anda menggunakan operator koma secara valid, tetapi Anda ingin memastikan bahwa tidak ada operator koma yang ditentukan pengguna, karena misalnya Anda mengandalkan titik-titik urutan antara sisi kiri dan kanan atau ingin memastikan tidak ada yang mengganggu yang diinginkan. tindakan. Di sinilah void() masuk ke dalam game:

for(T i, j; can_continue(i, j); ++i, void(), ++j)
  do_code(i, j);

Abaikan penampung yang saya tempatkan untuk kondisi dan kode. Yang penting adalah void(), yang membuat kekuatan kompiler menggunakan operator koma builtin. Ini bisa berguna ketika mengimplementasikan kelas-kelas sifat, kadang-kadang juga.

Array inisialisasi dalam konstruktor. Sebagai contoh di kelas jika kita memiliki array int sebagai:

class clName
{
  clName();
  int a[10];
};

Kita dapat menginisialisasi semua elemen dalam array ke default (di sini semua elemen array menjadi nol) di konstruktor sebagai:

clName::clName() : a()
{
}
28
Poorna

Oooh, saya bisa membuat daftar benci hewan peliharaan sebagai gantinya:

  • Destructors harus virtual jika Anda bermaksud menggunakan secara polimorfik
  • Terkadang anggota diinisialisasi secara default, terkadang tidak
  • Klas lokal tidak dapat digunakan sebagai parameter templat (membuatnya kurang berguna)
  • penentu pengecualian: terlihat berguna, tetapi tidak
  • fungsi kelebihan menyembunyikan fungsi kelas dasar dengan tanda tangan yang berbeda.
  • tidak ada standardisasi yang berguna untuk internasionalisasi (charset lebar standar portabel, siapa pun? Kita harus menunggu hingga C++ 0x)

Di sisi positifnya

  • fitur tersembunyi: blok coba fungsi. Sayangnya saya belum menemukan gunanya. Ya saya tahu mengapa mereka menambahkannya, tetapi Anda harus memikirkan kembali konstruktor yang membuatnya tidak ada gunanya.
  • Ada baiknya melihat dengan hati-hati pada jaminan STL tentang validitas iterator setelah modifikasi wadah, yang dapat memungkinkan Anda membuat beberapa loop yang sedikit lebih bagus.
  • Boost - ini bukan rahasia tapi layak untuk digunakan.
  • Pengembalian nilai optimasi (tidak jelas, tetapi secara khusus diizinkan oleh standar)
  • Functors alias objek fungsi alias operator (). Ini digunakan secara luas oleh STL. bukan benar-benar rahasia, tetapi merupakan efek samping yang bagus dari overloading dan template operator.
27
Robert

Anda dapat mengakses data yang dilindungi dan fungsi anggota dari kelas apa pun, tanpa perilaku yang tidak ditentukan, dan dengan semantik yang diharapkan. Baca terus untuk mengetahui caranya. Baca juga laporan kerusakan tentang ini.

Biasanya, C++ melarang Anda untuk mengakses anggota objek kelas yang dilindungi non-statis, bahkan jika kelas itu adalah kelas dasar Anda

struct A {
protected:
    int a;
};

struct B : A {
    // error: can't access protected member
    static int get(A &x) { return x.a; }
};

struct C : A { };

Itu terlarang: Anda dan kompiler tidak tahu apa referensi sebenarnya menunjuk. Itu bisa berupa objek C, di mana kelas B tidak memiliki bisnis dan petunjuk tentang datanya. Akses semacam itu hanya diberikan jika x adalah referensi ke kelas turunan atau turunan dari itu. Dan itu dapat memungkinkan sepotong kode sewenang-wenang untuk membaca setiap anggota yang dilindungi dengan hanya membuat kelas "membuang" yang membacakan anggota, misalnya std::stack:

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        static std::deque<int> &get(std::stack<int> &s) {
            // error: stack<int>::c is protected
            return s.c;
        }
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = pillager::get(s);
}

Tentunya, seperti yang Anda lihat ini akan menyebabkan kerusakan terlalu banyak. Tapi sekarang, petunjuk anggota memungkinkan menghindari perlindungan ini! Poin kuncinya adalah bahwa jenis pointer anggota terikat ke kelas yang benar-benar berisi kata anggota - tidak ke kelas yang Anda tentukan saat mengambil alamat. Ini memungkinkan kita untuk menghindari pengecekan

struct A {
protected:
    int a;
};

struct B : A {
    // valid: *can* access protected member
    static int get(A &x) { return x.*(&B::a); }
};

struct C : A { };

Dan tentu saja, ini juga berfungsi dengan std::stack contoh.

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        static std::deque<int> &get(std::stack<int> &s) {
            return s.*(pillager::c);
        }
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = pillager::get(s);
}

Itu akan menjadi lebih mudah dengan menggunakan deklarasi di kelas turunan, yang membuat nama anggota publik dan merujuk ke anggota kelas dasar.

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        using std::stack<int>::c;
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = s.*(&pillager::c);
}
27

Fitur tersembunyi:

  1. Fungsi virtual murni dapat memiliki implementasi. Contoh umum, destruktor virtual murni.
  2. Jika suatu fungsi melempar pengecualian yang tidak tercantum dalam spesifikasi pengecualiannya, tetapi fungsi tersebut memiliki std::bad_exception dalam spesifikasi pengecualiannya, pengecualian diubah menjadi std::bad_exception dan dibuang secara otomatis. Dengan begitu Anda setidaknya akan tahu bahwa bad_exception terlempar. Baca lebih lanjut di sini .

  3. fungsi coba blok

  4. Kata kunci templat di disambiguasi typedef dalam templat kelas. Jika nama spesialisasi templat anggota muncul setelah ., ->, atau :: operator, dan nama itu memiliki parameter template yang memenuhi syarat secara eksplisit, awali nama templat anggota dengan templat kata kunci. Baca lebih lanjut di sini .

  5. default parameter fungsi dapat diubah saat runtime. Baca lebih lanjut di sini .

  6. A[i] bekerja sebaik i[A]

  7. Contoh sementara kelas dapat dimodifikasi! Fungsi non-const member dapat dipanggil pada objek sementara. Sebagai contoh:

    struct Bar {
      void modify() {}
    }
    int main (void) {
      Bar().modify();   /* non-const function invoked on a temporary. */
    }
    

    Baca lebih lanjut di sini .

  8. Jika ada dua jenis yang berbeda sebelum dan sesudah : di ternary (?:) ekspresi operator, maka jenis ekspresi yang dihasilkan adalah yang paling umum dari keduanya. Sebagai contoh:

    void foo (int) {}
    void foo (double) {}
    struct X {
      X (double d = 0.0) {}
    };
    void foo (X) {} 
    
    int main(void) {
      int i = 1;
      foo(i ? 0 : 0.0); // calls foo(double)
      X x;
      foo(i ? 0.0 : x);  // calls foo(X)
    }
    
26
Sumant

Fitur tersembunyi lainnya adalah Anda dapat memanggil objek kelas yang dapat dikonversi ke pointer fungsi atau referensi. Resolusi kelebihan dilakukan pada mereka, dan argumen diteruskan dengan sempurna.

template<typename Func1, typename Func2>
class callable {
  Func1 *m_f1;
  Func2 *m_f2;

public:
  callable(Func1 *f1, Func2 *f2):m_f1(f1), m_f2(f2) { }
  operator Func1*() { return m_f1; }
  operator Func2*() { return m_f2; }
};

void foo(int i) { std::cout << "foo: " << i << std::endl; }
void bar(long il) { std::cout << "bar: " << il << std::endl; }

int main() {
  callable<void(int), void(long)> c(foo, bar);
  c(42); // calls foo
  c(42L); // calls bar
}

Ini disebut "fungsi panggilan pengganti".

map::operator[] membuat entri jika kunci tidak ada dan mengembalikan referensi ke nilai entri bawaan yang dibangun. Jadi, Anda dapat menulis:

map<int, string> m;
string& s = m[42]; // no need for map::find()
if (s.empty()) { // assuming we never store empty values in m
  s.assign(...);
}
cout << s;

Saya kagum pada berapa banyak programmer C++ tidak tahu ini.

24
Constantin

Menempatkan fungsi atau variabel dalam namespace tanpa nama mencela penggunaan static untuk membatasi mereka ke ruang lingkup file.

20
Jim Hunziker

Menentukan fungsi teman biasa di templat kelas perlu perhatian khusus:

template <typename T> 
class Creator { 
    friend void appear() {  // a new function ::appear(), but it doesn't 
        …                   // exist until Creator is instantiated 
    } 
};
Creator<void> miracle;  // ::appear() is created at this point 
Creator<double> oops;   // ERROR: ::appear() is created a second time! 

Dalam contoh ini, dua instansiasi yang berbeda membuat dua definisi yang identik — pelanggaran langsung terhadap ODR

Karena itu kita harus memastikan parameter templat templat kelas muncul dalam jenis fungsi teman yang didefinisikan dalam templat itu (kecuali jika kita ingin mencegah lebih dari satu instantiasi templat kelas dalam file tertentu, tetapi ini agak tidak mungkin). Mari kita terapkan ini pada variasi dari contoh kita sebelumnya:

template <typename T> 
class Creator { 
    friend void feed(Creator<T>*){  // every T generates a different 
        …                           // function ::feed() 
    } 
}; 

Creator<void> one;     // generates ::feed(Creator<void>*) 
Creator<double> two;   // generates ::feed(Creator<double>*) 

Penafian: Saya telah menempel bagian ini dari C++ Templates: The Complete Guide /Bagian 8.4

19
Özgür

fungsi void dapat mengembalikan nilai void

Sedikit diketahui, tetapi kode berikut baik-baik saja

void f() { }
void g() { return f(); }

Serta yang tampak aneh berikut ini

void f() { return (void)"i'm discarded"; }

Mengetahui hal ini, Anda bisa memanfaatkan di beberapa daerah. Salah satu contoh: void fungsi tidak dapat mengembalikan nilai tetapi Anda juga tidak bisa hanya mengembalikan apa-apa, karena mereka dapat dipakai dengan non-void. Alih-alih menyimpan nilai ke dalam variabel lokal, yang akan menyebabkan kesalahan untuk void, kembalikan nilai secara langsung

template<typename T>
struct sample {
  // assume f<T> may return void
  T dosomething() { return f<T>(); }

  // better than T t = f<T>(); /* ... */ return t; !
};

Baca file menjadi vektor string:

 vector<string> V;
 copy(istream_iterator<string>(cin), istream_iterator<string>(),
     back_inserter(V));

istream_iterator

17
Jason Baker

Salah satu tata bahasa paling menarik dari semua bahasa pemrograman.

Tiga dari hal-hal ini menjadi satu, dan dua adalah sesuatu yang sama sekali berbeda ...

SomeType t = u;
SomeType t(u);
SomeType t();
SomeType t;
SomeType t(SomeType(u));

Semua kecuali yang ketiga dan kelima mendefinisikan objek SomeType pada stack dan menginisialisasinya (dengan u dalam dua kasus pertama, dan konstruktor default di yang keempat. Yang ketiga mendeklarasikan fungsi yang tidak mengambil parameter dan mengembalikan SomeType. Kelima juga mendeklarasikan fungsi yang mengambil satu parameter dengan nilai tipe SomeType bernama u.

14
Eclipse

Anda dapat mem-template bitfield.

template <size_t X, size_t Y>
struct bitfield
{
    char left  : X;
    char right : Y;
};

Saya belum datang dengan tujuan apa pun untuk ini, tetapi tentu saja mengejutkan saya.

14
Kaz Dragon

Aturan dominasi bermanfaat, tetapi sedikit diketahui. Dikatakan bahwa meskipun dalam jalur non-unik melalui kisi kelas-dasar, pencarian nama untuk anggota yang sebagian tersembunyi adalah unik jika anggota tersebut milik kelas dasar virtual:

struct A { void f() { } };

struct B : virtual A { void f() { cout << "B!"; } };
struct C : virtual A { };

// name-lookup sees B::f and A::f, but B::f dominates over A::f !
struct D : B, C { void g() { f(); } };

Saya telah menggunakan ini untuk menerapkan keselarasan-dukungan yang secara otomatis menentukan keselarasan paling ketat dengan menggunakan aturan dominasi.

Ini tidak hanya berlaku untuk fungsi virtual, tetapi juga untuk nama typedef, anggota statis/non-virtual dan yang lainnya. Saya pernah melihatnya digunakan untuk menerapkan sifat-sifat yang dapat ditimpa dalam meta-program.

Operator kondisional ternary ?: mengharuskan operan kedua dan ketiga untuk memiliki tipe "menyenangkan" (berbicara secara informal). Tetapi persyaratan ini memiliki satu pengecualian (maksud kata): baik operan kedua atau ketiga dapat berupa ekspresi throw (yang bertipe void), terlepas dari jenis operan lainnya.

Dengan kata lain, seseorang dapat menulis ekspresi C++ yang valid berikut dengan menggunakan ?: operator

i = a > b ? a : throw something();

BTW, fakta bahwa melempar ekspresi sebenarnya ekspresi (bertipe void) dan bukan pernyataan adalah fitur lain yang kurang dikenal dari bahasa C++. Ini berarti, antara lain, bahwa kode berikut ini benar-benar valid

void foo()
{
  return throw something();
}

walaupun tidak banyak gunanya melakukannya dengan cara ini (mungkin dalam beberapa kode templat umum ini mungkin berguna).

12
AnT

Menyingkirkan deklarasi maju:

struct global
{
     void main()
     {
           a = 1;
           b();
     }
     int a;
     void b(){}
}
singleton;

Menulis pernyataan beralih dengan?: Operator:

string result = 
    a==0 ? "zero" :
    a==1 ? "one" :
    a==2 ? "two" :
    0;

Melakukan semuanya dalam satu baris:

void a();
int b();
float c = (a(),b(),1.0f);

Mem-kosongkan struct tanpa memset:

FStruct s = {0};

Nilai normalisasi/pembungkus sudut dan waktu:

int angle = (short)((+180+30)*65536/360) * 360/65536; //==-150

Menetapkan referensi:

struct ref
{
   int& r;
   ref(int& r):r(r){}
};
int b;
ref a(b);
int c;
*(int**)&a = &c;
12
AareP

Saya menemukan blog ini sebagai sumber yang luar biasa tentang arcan C++: C++ Truths .

9
Drealmer

Rahasia berbahaya adalah

Fred* f = new(ram) Fred(); http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.10
f->~Fred();

Rahasia favorit saya yang jarang saya lihat:

class A
{
};

struct B
{
  A a;
  operator A&() { return a; }
};

void func(A a) { }

int main()
{
  A a, c;
  B b;
  a=c;
  func(b); //yeah baby
  a=b; //gotta love this
}
8
user34537

Kelas lokal luar biasa:

struct MyAwesomeAbstractClass
{ ... };


template <typename T>
MyAwesomeAbstractClass*
create_awesome(T param)
{
    struct ans : MyAwesomeAbstractClass
    {
        // Make the implementation depend on T
    };

    return new ans(...);
}

cukup rapi, karena tidak mencemari namespace dengan definisi kelas yang tidak berguna ...

8
Alexandre C.

Salah satu fitur tersembunyi, bahkan disembunyikan ke pengembang GCC , adalah menginisialisasi anggota array menggunakan string literal. Misalkan Anda memiliki struktur yang perlu bekerja dengan array C, dan Anda ingin menginisialisasi anggota array dengan konten default

struct Person {
  char name[255];
  Person():name("???") { }
};

Ini berfungsi, dan hanya bekerja dengan array char dan inisialisasi string literal. Tidak perlu strcpy!

Salah satu contoh dari banyak: metaprogramming template. Tidak ada seorang pun di komite standar yang bermaksud bahwa ada sub-bahasa Turing-lengkap yang dijalankan pada waktu kompilasi.

Metaprogramming template bukan fitur tersembunyi. Bahkan di perpustakaan pendorong. Lihat MPL . Tetapi jika "hampir tersembunyi" cukup bagus, maka lihat boost libraries . Ini berisi banyak barang yang tidak mudah diakses tanpa dukungan perpustakaan yang kuat.

Salah satu contohnya adalah boost.lambda library, yang menarik karena C++ tidak memiliki fungsi lambda dalam standar saat ini.

Contoh lain adalah Loki , yang "membuat banyak penggunaan metaprogramming template C++ dan mengimplementasikan beberapa alat yang biasa digunakan: pengetikkan, functor, singleton, pointer pintar, pabrik objek, pengunjung dan multimethods." [ Wikipedia ]

6
Markowitch

Tidak ada fitur tersembunyi, tetapi bahasa C++ sangat kuat dan sering kali bahkan pengembang standar tidak dapat membayangkan untuk apa C++ dapat digunakan.

Sebenarnya dari konstruksi bahasa yang cukup sederhana Anda dapat menulis sesuatu yang sangat kuat. Banyak hal seperti itu tersedia di www.boost.org sebagai contoh (dan http://www.boost.org/doc/libs/1_36_0/doc/html/lambda.html di antara mereka ).

Untuk memahami cara bagaimana konstruksi bahasa sederhana dapat digabungkan dengan sesuatu yang kuat, ada baiknya dibaca "C++ Templates: The Complete Guide" oleh David Vandevoorde, Nicolai M. Josuttis dan benar-benar buku ajaib "Desain C++ Modern ..." oleh Andrei Alexandresc .

Dan akhirnya, sulit untuk belajar C++, Anda harus mencoba mengisinya;)

5
sergtk

Menurut saya hanya sedikit orang yang tahu tentang ruang nama yang tidak disebutkan namanya:

namespace {
  // Classes, functions, and objects here.
}

Ruang nama yang tidak disebutkan namanya berperilaku seolah-olah mereka digantikan oleh:

namespace __unique_{ /* empty body */ }
using namespace __unique_name__;
namespace __unique_{
  // original namespace body
}

".. di mana semua kemunculan [nama unik ini] dalam unit terjemahan digantikan oleh pengidentifikasi yang sama dan pengidentifikasi ini berbeda dari semua pengidentifikasi lainnya di seluruh program." [C++ 03, 7.3.1.1/1]

4
vobject
4
Özgür

Saya tidak yakin tentang disembunyikan, tetapi ada beberapa menarik'trik' yang mungkin tidak jelas dari hanya membaca spek.

3
dbrien

Ada banyak "perilaku tidak terdefinisi". Anda dapat belajar bagaimana menghindari mereka membaca buku-buku bagus dan membaca standar.

3
ugasoft

Kebanyakan pengembang C++ mengabaikan kekuatan metaprogramming template. Periksa Loki Libary . Ini mengimplementasikan beberapa alat canggih seperti pengetik, functor, singleton, smart pointer, pabrik objek, pengunjung dan metode multimetode menggunakan metaprogramming template secara luas (dari wikipedia ). Sebagian besar Anda dapat menganggap ini sebagai fitur "c ++" tersembunyi.

3
Sridhar Iyer

Dari C++ Truths .

Mendefinisikan fungsi yang memiliki tanda tangan identik dalam cakupan yang sama, jadi ini sah:

template<class T> // (a) a base template
void f(T) {
  std::cout << "f(T)\n";
}

template<>
void f<>(int*) { // (b) an explicit specialization
  std::cout << "f(int *) specilization\n";
}

template<class T> // (c) another, overloads (a)
void f(T*) {
  std::cout << "f(T *)\n";
}

template<>
void f<>(int*) { // (d) another identical explicit specialization
  std::cout << "f(int *) another specilization\n";
}
3
Özgür
  • pointer ke metode kelas
  • Kata kunci "ketik nama"
3
shoosh
3
sdcvvc

main () tidak perlu nilai kembali:

int main(){}

adalah program C++ terpendek yang valid.

2
Jeffrey Faust

Perhatikan perbedaan antara pointer fungsi gratis dan inisialisasi fungsi pointer anggota:

fungsi anggota:

struct S
{
 void func(){};
};
int main(){
void (S::*pmf)()=&S::func;//  & is mandatory
}

dan fungsi gratis:

void func(int){}
int main(){
void (*pf)(int)=func; // & is unnecessary it can be &func as well; 
}

Berkat ini berlebihan &, Anda dapat menambahkan manipulator aliran - yang merupakan fungsi bebas - tanpa rantai:

cout<<hex<<56; //otherwise you would have to write cout<<&hex<<56, not neat.
2
Özgür
  1. map::insert(std::pair(key, value)); tidak menimpa jika nilai kunci sudah ada.

  2. Anda dapat instantiate sebuah kelas tepat setelah definisinya: (Saya dapat menambahkan bahwa fitur ini telah memberi saya ratusan kesalahan kompilasi karena titik koma yang hilang, dan saya belum pernah melihat orang menggunakan ini di kelas)

    class MyClass {public: /* code */} myClass;
    
2
Viktor Sehr

Ada banyak konstruksi "rumit" di C++. Mereka berangkat dari implementasi "sederhana" dari kelas tertutup/akhir menggunakan warisan virtual. Dan dapatkan konstruksi pemrograman meta yang "rumit" seperti Boost's MPL ( tutorial ). Kemungkinan untuk menembak diri sendiri tidak ada habisnya, tetapi jika terus di cek (mis. Programmer berpengalaman), berikan beberapa fleksibilitas terbaik dalam hal rawatan dan kinerja.

1
Amir

Kelas dan kunci kelas struct hampir identik. Perbedaan utama adalah bahwa kelas-kelas default untuk akses pribadi untuk anggota dan pangkalan, sementara struct standar untuk publik:

// this is completely valid C++:
class A;
struct A { virtual ~A() = 0; };
class B : public A { public: virtual ~B(); };

// means the exact same as:
struct A;
class A { public: virtual ~A() = 0; };
struct B : A { virtual ~B(); };

// you can't even tell the difference from other code whether 'struct'
// or 'class' was used for A and B

Serikat pekerja juga dapat memiliki anggota dan metode, dan standar untuk akses publik mirip dengan struct.

1
a_m0d

Idiom Konversi Tidak Langsung :

Misalkan Anda merancang kelas pointer cerdas. Selain kelebihan operator * dan ->, kelas penunjuk cerdas biasanya mendefinisikan operator konversi menjadi bool:

template <class T>
class Ptr
{
public:
 operator bool() const
 {
  return (rawptr ? true: false);
 }
//..more stuff
private:
 T * rawptr;
};

Konversi ke bool memungkinkan klien untuk menggunakan pointer pintar dalam ekspresi yang memerlukan operan bool:

Ptr<int> ptr(new int);
if(ptr ) //calls operator bool()
 cout<<"int value is: "<<*ptr <<endl;
else
 cout<<"empty"<<endl;

Selain itu, konversi implisit ke bool diperlukan dalam deklarasi bersyarat seperti:

if (shared_ptr<X> px = dynamic_pointer_cast<X>(py))
{
 //we get here only of px isn't empty
} 

Sayangnya, konversi otomatis ini membuka gerbang ke kejutan yang tidak diinginkan:

Ptr <int> p1;
Ptr <double> p2;

//surprise #1
cout<<"p1 + p2 = "<< p1+p2 <<endl; 
//prints 0, 1, or 2, although there isn't an overloaded operator+()

Ptr <File> pf;
Ptr <Query> pq; // Query and File are unrelated 

//surprise #2
if(pf==pq) //compares bool values, not pointers! 

Solusi: Gunakan idiom "konversi tidak langsung", dengan konversi dari pointer ke anggota data [pMember] menjadi bool sehingga hanya akan ada 1 konversi implisit, yang akan mencegah perilaku tak terduga yang disebutkan di atas: pMember-> bool daripada bool-> sesuatu lain.

1
Özgür

Jika operator delete () mengambil argumen ukuran selain * void, itu artinya, sangat, akan menjadi kelas dasar. Argumen ukuran itu memungkinkan memeriksa ukuran tipe untuk menghancurkan yang benar. Here what Stephen Dewhurst memberi tahu tentang ini:

Perhatikan juga bahwa kami telah menggunakan versi dua argumen penghapusan operator daripada versi satu argumen biasa. Versi dua argumen ini adalah versi "biasa" dari penghapusan operator anggota yang sering digunakan oleh kelas dasar yang mengharapkan kelas turunan mewarisi implementasi penghapusan operator mereka. Argumen kedua akan berisi ukuran objek yang dihapus — informasi yang sering berguna dalam menerapkan manajemen memori kustom.

1
Özgür

Saya menemukan instasi template rekursif cukup keren:

template<class int>
class foo;

template
class foo<0> {
    int* get<0>() { return array; }
    int* array;  
};

template<class int>
class foo<i> : public foo<i-1> {
    int* get<i>() { return array + 1; }  
};

Saya telah menggunakannya untuk menghasilkan kelas dengan 10-15 fungsi yang mengembalikan pointer ke berbagai bagian array, karena API yang saya gunakan membutuhkan satu pointer fungsi untuk setiap nilai.

Yaitu. memprogram kompiler untuk menghasilkan banyak fungsi, melalui rekursi. Mudah seperti pai. :)

1
Macke

Favorit saya (untuk saat ini) adalah kurangnya sematika dalam pernyataan seperti A = B = C. Apa nilai A pada dasarnya tidak ditentukan.

Pikirkan ini:

class clC
{
public:
   clC& operator=(const clC& other)
   {
      //do some assignment stuff
      return copy(other);
   }
   virtual clC& copy(const clC& other);
}

class clB : public clC
{
public:
  clB() : m_copy()
  {
  }

  clC& copy(const clC& other)
  {
    return m_copy;
  }

private:
  class clInnerB : public clC
  {
  }
  clInnerB m_copy;
}

sekarang A mungkin dari jenis yang tidak dapat diakses oleh selain objek tipe CLB dan memiliki nilai yang tidak terkait dengan C.

0
Rune FS

Menambahkan kendala ke templat.

0
Özgür

Pointer anggota dan operator pointer anggota -> *

#include <stdio.h>
struct A { int d; int e() { return d; } };
int main() {
    A* a = new A();
    a->d = 8;
    printf("%d %d\n", a ->* &A::d, (a ->* &A::e)() );
    return 0;
}

Untuk metode (a -> * & A :: e) () mirip dengan Function.call () dari javascript

var f = A.e
f.call(a) 

Untuk anggota, ini seperti mengakses dengan operator []

a['d']
0
Kamil Szot

Anda dapat melihat semua makro yang telah ditentukan melalui sakelar baris perintah dengan beberapa kompiler. Ini bekerja dengan gcc dan icc (kompiler C++ Intel):

$ touch empty.cpp
$ g++ -E -dM empty.cpp | sort >gxx-macros.txt
$ icc -E -dM empty.cpp | sort >icx-macros.txt
$ touch empty.c
$ gcc -E -dM empty.c | sort >gcc-macros.txt
$ icc -E -dM empty.c | sort >icc-macros.txt

Untuk MSVC mereka terdaftar di tempat tunggal . Mereka dapat didokumentasikan dalam satu tempat untuk yang lain juga, tetapi dengan perintah di atas Anda dapat dengan jelas melihat apa yang didefinisikan dan tidak didefinisikan dan apa nilai digunakan, setelah menerapkan semua switch baris perintah lainnya.

Bandingkan (setelah disortir):

 $ diff gxx-macros.txt icx-macros.txt
 $ diff gxx-macros.txt gcc-macros.txt
 $ diff icx-macros.txt icc-macros.txt
0
Roger Pate
class Empty {};

namespace std {
  // #1 specializing from std namespace is okay under certain circumstances
  template<>
  void swap<Empty>(Empty&, Empty&) {} 
}

/* #2 The following function has no arguments. 
   There is no 'unknown argument list' as we do
   in C.
*/
void my_function() { 
  cout << "whoa! an error\n"; // #3 using can be scoped, as it is in main below
  // and this doesn't affect things outside of that scope
}

int main() {
  using namespace std; /* #4 you can use using in function scopes */
  cout << sizeof(Empty) << "\n"; /* #5 sizeof(Empty) is never 0 */
  /* #6 falling off of main without an explicit return means "return 0;" */
}
0
dirkgently