C ++ decltype dan tanda kurung - mengapa?

32

Subjek telah dibahas sebelumnya , tetapi ini bukan duplikat.

Ketika seseorang bertanya tentang perbedaan antara decltype(a)dan decltype((a)), jawaban yang biasa adalah - aadalah variabel, (a)adalah ekspresi. Saya menemukan jawaban ini tidak memuaskan.

Pertama, aadalah ekspresi juga. Pilihan untuk ekspresi primer meliputi, antara lain -

  • (ekspresi)
  • ekspresi-id

Lebih penting lagi, frasa untuk decltype menganggap kurung sangat, sangat eksplisit :

For an expression e, the type denoted by decltype(e) is defined as follows:
(1.1)  if e is an unparenthesized id-expression naming a structured binding, ...
(1.2)  otherwise, if e is an unparenthesized id-expression naming a non-type template-parameter, ...
(1.3)  otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, ...
(1.4)  otherwise, ...

Jadi pertanyaannya tetap. Mengapa kurung diperlakukan berbeda? Adakah yang akrab dengan makalah teknis atau diskusi komite di belakangnya? Pertimbangan eksplisit untuk tanda kurung membuat orang berpikir ini bukan kekhilafan, jadi pasti ada alasan teknis saya tidak ada.

Ofek Shilon
sumber
2
"jawaban yang biasa adalah - a adalah variabel, (a) adalah ekspresi" Apa yang mereka maksudkan " (a)adalah ekspresi, dan amerupakan ekspresi dan variabel".
HolyBlackCat

Jawaban:

18

Itu bukan kekhilafan. Sangat menarik, bahwa dalam Decltype dan otomatis (revisi 4) (N1705 = 04-0145) ada pernyataan:

Aturan decltype sekarang secara eksplisit menyatakan itu decltype((e)) == decltype(e)(seperti yang disarankan oleh EWG).

Tetapi dalam Decltype (revisi 6): kata-kata yang diusulkan (N2115 = 06-018) salah satu perubahannya adalah

Ekspresi terkurung di dalam decltype tidak dianggap sebagai id-expression.

Tidak ada alasan dalam pengkalimatannya, tetapi saya kira ini adalah jenis perpanjangan dari jenis pernyataan menggunakan sintaks yang sedikit berbeda, dengan kata lain, itu dimaksudkan untuk membedakan kasus-kasus ini.

Penggunaan untuk itu ditunjukkan dalam C ++ draft9.2.8.4:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17;        // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

Yang benar-benar menarik, adalah cara kerjanya dengan returnpernyataan:

decltype(auto) f()
{
    int i{ 0 };
    return (i);
}

Visual Studio 2019 saya menyarankan saya untuk menghapus tanda kurung yang berlebihan, tetapi sebenarnya mereka berubah menjadi decltype((i))perubahan yang mengembalikan nilai int&yang menjadikannya UB sejak mengembalikan referensi ke variabel lokal.

espkk
sumber
Terima kasih telah menunjukkan asal-usul! Tidak hanya bisa menyatakan jenis telah ditentukan secara berbeda, ternyata itu awalnya. Dokumen rev-6 secara eksplisit menambahkan perilaku kurung lucu ini, tetapi melewatkan alasan :(. Saya kira itu sedekat jawaban yang kita dapatkan sekarang
Ofek Shilon
Namun tidak ada yang mengusulkan untuk menyelesaikan masalah konyol dengan membuatnya menjadi dua kata kunci yang berbeda, untuk dua fungsi yang berbeda? Salah satu kesalahan terbesar komite (yang secara historis membuat banyak kesalahan sendiri).
curiousguy
13

Mengapa kurung diperlakukan berbeda?

Kurung tidak diperlakukan secara berbeda. Ini adalah ekspresi-id yang tidak beradab yang diperlakukan berbeda.

Ketika tanda kurung hadir maka aturan reguler untuk semua ekspresi berlaku. Kategori dan kategori nilai diekstraksi dan dikodifikasi dalam tipe decltype.

Ketentuan khusus ada di sana sehingga kami dapat menulis kode berguna lebih mudah. Saat menerapkan decltypeke nama variabel (anggota), kami biasanya tidak ingin beberapa tipe yang mewakili properti variabel ketika diperlakukan sebagai ekspresi. Alih-alih, kami hanya ingin jenis variabel dideklarasikan dengan, tanpa harus menerapkan satu ton ciri tipe untuk mendapatkannya. Dan itulah tepatnya yang decltypeditentukan untuk diberikan kepada kita.

Jika kita benar-benar peduli pada properti variabel sebagai ekspresi, maka kita masih bisa mendapatkannya dengan mudah, dengan sepasang kurung tambahan.

StoryTeller - Unslander Monica
sumber
Jadi untuk intanggota idari a, decltype(a.i)yang intsementara decltype((a.i))ini int&(dengan asumsi atidak const)? Karena ungkapan a.iitu dapat ditentukan?
n314159
1
@ n314159 - Itulah intinya. Ekspresi a.iadalah nilai non-const, sehingga Anda mendapatkan tipe referensi nilai non-const (a.i).
StoryTeller - Unslander Monica
Mengapa 'aturan reguler untuk semua ekspresi' menunjukkan bahwa tipe mereka adalah referensi? Mengapa 'properti variabel sebagai ekspresi' menyertakan properti menjadi referensi? Apakah ini default alami?
Ofek Shilon
1
@OfekShilon - Tipe mereka bukan referensi. Jenis ekspresi tidak pernah dianalisis sebagai referensi . Tetapi dectlype hanya dapat menyelesaikan untuk suatu jenis, dan itu dimaksudkan untuk memberi tahu kita bukan hanya jenis ekspresi, tetapi juga kategori nilainya. Kategori nilai dikodifikasikan oleh jenis referensi. nilai adalah &, nilai adalah &&, dan nilai bukan jenis referensi.
StoryTeller - Unslander Monica
@StoryTeller tepatnya, tidak ada perbedaan 'alami' antara jenis ekspresi dan non ekspresi. Yang membuat perbedaan menjadi artifisial.
Ofek Shilon
1

Pra C ++ 11 bahasa membutuhkan alat untuk mendapatkan dua jenis informasi yang berbeda :

  • jenis ekspresi
  • jenis variabel seperti yang dideklarasikan

Karena sifat informasi ini, fitur harus ditambahkan dalam bahasa (tidak dapat dilakukan di perpustakaan). Itu berarti kata kunci baru. Standar dapat memperkenalkan dua kata kunci baru untuk ini. Misalnya exprtypeuntuk mendapatkan jenis ekspresi dan decltypeuntuk mendapatkan tipe deklarasi dari suatu variabel. Itu akan menjadi pilihan yang jelas dan bahagia.

Namun komite standar selalu berusaha sekuat tenaga untuk menghindari memasukkan kata kunci baru ke dalam bahasa untuk meminimalkan kerusakan kode lama. Kompatibilitas mundur adalah filosofi inti bahasa.

Jadi dengan C ++ 11 kami mendapat hanya satu kata kunci yang digunakan untuk dua hal yang berbeda: decltype. Cara membedakan antara kedua kegunaan adalah dengan memperlakukan secara decltype(id-expression)berbeda. Itu adalah keputusan sadar oleh komite, kompromi (kecil).

bolov
sumber
Saya ingat pernah mendengar ini di pembicaraan cpp. Namun saya tidak memiliki harapan untuk menemukan sumbernya. Akan lebih bagus jika seseorang menemukannya.
bolov
1
C ++ secara historis menambahkan banyak kata kunci baru, banyak tanpa garis bawah dan beberapa dengan satu kata bahasa Inggris. Rasionalnya benar-benar absurd.
curiousguy
@curiousguy setiap kata kunci yang diperkenalkan akan berbenturan dengan simbol-simbol pengguna yang sudah ada sehingga panitia membebani keputusan untuk menambahkan kata kunci yang baru dipesan ke dalam bahasa. Ini bukan imo yang absurd.
bolov
Tidak benar: exportdiperkenalkan. Jika Anda dapat memiliki export(sebelumnya semua templat secara default "diekspor"), Anda dapat memiliki hal-hal seperti decltypedan constexpr. Jelas menambahkan registerdalam bahasa lain akan bermasalah.
curiousguy
@bolov Terima kasih! (1) Apakah ini spekulasi atau pengetahuan? Apakah Anda mengetahui adanya diskusi di mana menghindari kata kunci tambahan adalah motivasinya? (2) Dapatkah Anda memberikan contoh di mana ekspresi-id sebenarnya perlu diperlakukan secara berbeda jika digunakan "sebagai ekspresi"? (Apa artinya ini?)
Ofek Shilon