Mengapa ruang nama yang tidak disebutkan namanya digunakan dan apa manfaatnya?

242

Saya baru saja bergabung dengan proyek perangkat lunak C ++ baru dan saya mencoba memahami desainnya. Proyek ini sering menggunakan ruang nama yang tidak disebutkan namanya. Misalnya, sesuatu seperti ini dapat terjadi di file definisi kelas:

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

Apa pertimbangan desain yang mungkin menyebabkan seseorang menggunakan namespace yang tidak disebutkan namanya? Apa kelebihan dan kekurangannya?

Scottie T
sumber

Jawaban:

189

Ruang nama tanpa nama adalah utilitas untuk menjadikan unit terjemahan pengidentifikasi lokal. Mereka berperilaku seolah-olah Anda akan memilih nama unik per unit terjemahan untuk namespace:

namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

Langkah ekstra menggunakan tubuh kosong itu penting, jadi Anda sudah bisa merujuk di dalam tubuh namespace ke pengidentifikasi seperti ::nameyang didefinisikan dalam namespace itu, karena direktif penggunaan sudah terjadi.

Ini berarti Anda dapat memiliki fungsi gratis yang disebut (misalnya) helpyang dapat ada di beberapa unit terjemahan, dan mereka tidak akan berbenturan pada waktu tautan. Efeknya hampir identik dengan menggunakan statickata kunci yang digunakan dalam C yang dapat Anda masukkan ke dalam deklarasi pengidentifikasi. Ruang nama yang tidak disebutkan namanya adalah alternatif yang unggul, bahkan dapat membuat unit terjemahan jenis lokal.

namespace { int a1; }
static int a2;

Keduanya aadalah unit terjemahan lokal dan tidak akan berbenturan pada waktu tautan. Tetapi perbedaannya adalah bahwa a1dalam namespace anonim mendapatkan nama yang unik.

Baca artikel yang bagus di Comeau-Computing Mengapa namespace tanpa nama digunakan sebagai ganti statik? ( Archive.org mirror ).

Johannes Schaub - litb
sumber
Anda menjelaskan hubungannya dengan static. Bisakah Anda juga membandingkan dengan __attribute__ ((visibility ("hidden")))?
phinz
74

Memiliki sesuatu dalam namespace anonim berarti itu lokal untuk unit terjemahan ini (file .cpp dan semua termasuk) ini berarti bahwa jika simbol lain dengan nama yang sama didefinisikan di tempat lain tidak akan ada pelanggaran Aturan Definisi Satu (ODR).

Ini sama dengan cara C memiliki variabel global statis atau fungsi statis tetapi dapat digunakan untuk definisi kelas juga (dan harus digunakan daripada staticdi C ++).

Semua ruang nama anonim dalam file yang sama diperlakukan sebagai ruang nama yang sama dan semua ruang nama anonim dalam file yang berbeda berbeda. Namespace anonim sama dengan:

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;
Motti
sumber
14

Namespace yang tidak disebutkan namanya membatasi akses kelas, variabel, fungsi dan objek ke file yang didefinisikan. Fungsionalitas namespace yang tidak disebutkan namanya mirip dengan statickata kunci dalam C / C ++.
statickata kunci membatasi akses variabel global dan fungsi ke file di mana mereka didefinisikan.
Ada perbedaan antara namespace tanpa nama dan statickata kunci karena namespace yang tidak disebutkan namanya memiliki keunggulan dibandingkan statis. statickata kunci dapat digunakan dengan variabel, fungsi dan objek tetapi tidak dengan kelas yang ditentukan pengguna.
Sebagai contoh:

static int x;  // Correct 

Tapi,

static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong

Tetapi hal yang sama bisa terjadi dengan namespace yang tidak disebutkan namanya. Sebagai contoh,

 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct
Sachin
sumber
13

Selain jawaban lain untuk pertanyaan ini, menggunakan namespace anonim juga dapat meningkatkan kinerja. Karena simbol dalam namespace tidak memerlukan tautan eksternal, kompiler lebih bebas untuk melakukan optimasi kode secara agresif dalam namespace. Sebagai contoh, suatu fungsi yang disebut beberapa kali satu kali dalam satu lingkaran dapat digarisbawahi tanpa berdampak pada ukuran kode.

Sebagai contoh, pada sistem saya kode berikut ini memakan waktu sekitar 70% dari waktu berjalan jika namespace anonim digunakan (x86-64 gcc-4.6.3 dan -O2; perhatikan bahwa kode tambahan di add_val membuat kompiler tidak mau memasukkan dua kali).

#include <iostream>

namespace {
  double a;
  void b(double x)
  {
    a -= x;
  }
  void add_val(double x)
  {
    a += x;
    if(x==0.01) b(0);
    if(x==0.02) b(0.6);
    if(x==0.03) b(-0.1);
    if(x==0.04) b(0.4);
  }
}

int main()
{
  a = 0;
  for(int i=0; i<1000000000; ++i)
    {
      add_val(i*1e-10);
    }
  std::cout << a << '\n';
  return 0;
}
xioxox
sumber
5
Terlalu bagus untuk menjadi kenyataan - Saya mencoba segmen ini pada gcc 4-1-2, menggunakan optimasi O3, dengan-dan-tanpa pernyataan namespace: -> Mendapat waktu yang sama (3sec, dengan -O3, dan 4sec dengan -O3)
Theo
2
Kode ini sengaja dibuat rumit untuk mencoba meyakinkan kompiler untuk tidak memasukkan inline b dan menambahkan_val ke dalam main. Optimasi O3 menggunakan banyak inlining terlepas dari biaya untuk kode mengasapi. Namun, masih ada kemungkinan fungsi di mana O3 tidak akan inline add_val. Anda dapat mencoba membuat add_val lebih kompleks, atau memanggilnya berkali-kali dari utama dalam keadaan yang berbeda.
xioxox
5
@Aniel: apa yang saya lewatkan? seperti yang dibaca, Anda mengatakan Anda dibandingkan -O3dengan dirinya sendiri, maka Anda mengatakan 3 vs 4 detik adalah "waktu yang sama". tak satu pun dari ini masuk akal. saya curiga penjelasan sebenarnya akan, tetapi apa itu?
underscore_d
@underscore_d Jawabannya menyatakan -O2 digunakan dalam kedua kasus, bukan -O3. Level optimisasi yang berbeda mungkin berperilaku berbeda. Juga, versi kompiler yang berbeda mungkin berperilaku berbeda (jawabannya dapat menjadi usang, yaitu)
Paul Stelian
1
@PaulStelian Saya tahu itu, tetapi tampaknya cukup jelas bahwa saya membalas bukan untuk jawaban xioxox tetapi lebih kepada komentar Theo (meskipun namanya telah berubah atau entah bagaimana saya ikut campur)
underscore_d
12

Contoh tersebut menunjukkan bahwa orang-orang di proyek yang Anda ikuti tidak memahami ruang nama anonim :)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

Ini tidak perlu berada dalam namespace anonim, karena constobjek sudah memiliki hubungan statis dan karenanya tidak mungkin bertentangan dengan pengidentifikasi dari nama yang sama di unit terjemahan lain.

    bool getState(userType*,otherUserType*);
}

Dan ini sebenarnya pesimis: getState()memiliki hubungan eksternal. Biasanya lebih baik untuk memilih tautan statis, karena itu tidak mencemari tabel simbol. Lebih baik menulis

static bool getState(/*...*/);

sini. Saya jatuh ke dalam perangkap yang sama (ada kata-kata dalam standar yang menunjukkan bahwa file-statika entah bagaimana sudah ditinggalkan dalam mendukung ruang nama anonim), tetapi bekerja di proyek C ++ besar seperti KDE, Anda mendapatkan banyak orang yang memutar kepala Anda dengan cara yang benar berkeliling lagi :)

Marc Mutz - mmutz
sumber
10
Karena c ++ 11 ruang nama tanpa nama memiliki hubungan internal (bagian 3.5 dalam standar atau en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces )
Emile Vrijdags
11
"Ini tidak perlu berada dalam namespace anonim" Secara teknis, tentu - tetapi tetap saja, tidak ada salahnya untuk menempatkan mereka dalam satu, sebagai pengingat visual semantik mereka dan membuatnya (bahkan lebih) sepele untuk menghilangkan constness nanti jika diinginkan. Saya ragu itu berarti tim OP "tidak mengerti" apa pun! Juga, sedikit tentang fungsi di ruang nama anonim yang memiliki hubungan eksternal salah di C ++ 11 dan seterusnya seperti yang disebutkan. Menurut pemahaman saya, mereka memperbaiki masalah argumen templat yang sebelumnya membutuhkan tautan eksternal, sehingga dapat memungkinkan ruang nama yang tidak disebutkan namanya (dapat berisi argumen templat) memiliki tautan internal.
underscore_d
11

Namespace anonim membuat variabel terlampir, fungsi, kelas, dll. Hanya tersedia di dalam file itu. Dalam contoh Anda ini adalah cara untuk menghindari variabel global. Tidak ada perbedaan kinerja waktu runtime atau kompilasi.

Tidak ada banyak keuntungan atau kerugian selain dari "apakah saya ingin variabel, fungsi, kelas, dll ini menjadi publik atau pribadi?"

Max Lybbert
sumber
2
Mungkin ada perbedaan kinerja - lihat jawaban saya di sini. Ini memungkinkan kompiler untuk mengoptimalkan kode dengan lebih baik.
xioxox
2
Anda ada benarnya; setidaknya sejauh C ++ hari ini. Namun, C ++ 98 / C ++ 03 hal-hal yang diperlukan memiliki tautan eksternal agar dapat digunakan sebagai argumen templat. Karena hal-hal dalam ruang nama anonim tersedia sebagai argumen templat, mereka akan memiliki tautan eksternal (setidaknya dalam pra-C ++ 11) bahkan jika tidak ada cara untuk merujuknya dari luar file. Saya pikir mungkin ada beberapa kemampuan untuk mempermasalahkan hal itu, karena standar hanya mengharuskan hal-hal bertindak seolah-olah aturan itu ditegakkan; dan kadang-kadang mungkin untuk melakukannya tanpa benar-benar menegakkan aturan.
Max Lybbert