Apa logika di balik kata kunci "menggunakan" di C ++?

155

Apa logika di balik kata kunci "menggunakan" di C ++?

Ini digunakan dalam situasi yang berbeda dan saya mencoba untuk menemukan apakah semua itu memiliki kesamaan dan ada alasan mengapa kata kunci "menggunakan" digunakan seperti itu.

using namespace std; // to import namespace in the current namespace
using T = int; // type alias
using SuperClass::X; // using super class methods in derived class
pengguna3111311
sumber
56
Komite standar tidak suka memasukkan kata kunci baru ke dalam tata bahasa C ++.
Internet terbuat dari catz
4
@tehinternetsismadeofcatz Jika itu benar-benar logikanya, mohon maaf, saya akan pergi dan bunuh diri sekarang.
user3111311
64
@ user3111311: Anda benar-benar mengenali implikasi dari memperkenalkan kata-kata baru yang telah dipesan, bukan? Ini berarti bahwa semua kode yang ada yang menggunakannya sebagai nama pengenal tiba-tiba gagal untuk dikompilasi. Itu HAL YANG BURUK. Misalnya, ada banyak kode C yang tidak dapat dikompilasi sebagai C ++ karena mengandung hal-hal seperti int class;. Akan lebih buruk lagi jika kode C ++ tiba-tiba berhenti menjadi C ++ yang valid.
Ben Voigt
7
@BenVoigt: Fakta bahwa penggunaan kode C int class;tidak akan dikompilasi sebagai C ++ tidak sepenuhnya buruk. Ini dapat digunakan untuk menjamin bahwa kode C akan dikompilasi sebagai C. Terlalu mudah untuk melupakan bahwa C dan C ++ adalah dua bahasa yang berbeda - dan, secara praktis, ada kode yang valid C dan C ++ yang valid, tetapi dengan semantik yang berbeda.
Keith Thompson
3
Dalam hal itu, usingtidak lebih buruk (atau lebih baik) dari static. IMHO intinya tidak memperkenalkan kata kunci baru sangat penting karena dijelaskan oleh internet terbuat dari catz dan Ben Voigt.
Cassio Neri

Jawaban:

120

Di C ++ 11, usingkata kunci saat digunakan untuk type aliasidentik dengan typedef.

7.1.3.2

Nama typedef juga bisa dimasukkan dengan deklarasi alias. Pengenal yang mengikuti kata kunci yang digunakan menjadi nama-typedef dan atribut-penentu-seq opsional yang mengikuti pengenal yang ada pada nama-typedef itu. Ini memiliki semantik yang sama seolah-olah itu diperkenalkan oleh penentu typedef. Secara khusus, ini tidak mendefinisikan tipe baru dan tidak akan muncul di tipe-id.

Bjarne Stroustrup memberikan contoh praktis:

typedef void (*PFD)(double);    // C style typedef to make `PFD` a pointer to a function returning void and accepting double
using PF = void (*)(double);    // `using`-based equivalent of the typedef above
using P = [](double)->void; // using plus suffix return type, syntax error
using P = auto(double)->void // Fixed thanks to DyP

Sebelum C ++ 11, usingkata kunci dapat membawa fungsi anggota ke dalam cakupan. Di C ++ 11, Anda sekarang dapat melakukan ini untuk konstruktor (contoh Bjarne Stroustrup lainnya):

class Derived : public Base { 
public: 
    using Base::f;    // lift Base's f into Derived's scope -- works in C++98
    void f(char);     // provide a new f 
    void f(int);      // prefer this f to Base::f(int) 

    using Base::Base; // lift Base constructors Derived's scope -- C++11 only
    Derived(char);    // provide a new constructor 
    Derived(int);     // prefer this constructor to Base::Base(int) 
    // ...
}; 

Ben Voight memberikan alasan yang cukup bagus di balik alasan tidak memperkenalkan kata kunci baru atau sintaks baru. Standar ingin menghindari kerusakan kode lama sebanyak mungkin. Inilah sebabnya mengapa dalam dokumen usulan Anda akan melihat bagian seperti Impact on the Standard, Design decisions, dan bagaimana mereka dapat mempengaruhi kode yang lebih tua. Ada situasi ketika sebuah proposal tampak seperti ide yang sangat bagus tetapi mungkin tidak memiliki daya tarik karena akan terlalu sulit untuk diterapkan, terlalu membingungkan, atau akan bertentangan dengan kode lama.


Ini adalah kertas tua dari tahun 2003 n1449 . Alasannya tampaknya terkait dengan template. Peringatan: mungkin ada kesalahan ketik karena menyalin dari PDF.

Pertama mari kita pertimbangkan contoh mainan:

template <typename T>
class MyAlloc {/*...*/};

template <typename T, class A>
class MyVector {/*...*/};

template <typename T>

struct Vec {
typedef MyVector<T, MyAlloc<T> > type;
};
Vec<int>::type p; // sample usage

Masalah mendasar dengan idiom ini, dan fakta motivasi utama untuk proposal ini, adalah bahwa idiom tersebut menyebabkan parameter template muncul dalam konteks yang tidak dapat disimpulkan. Artinya, tidak mungkin memanggil fungsi foo di bawah ini tanpa menentukan argumen template secara eksplisit.

template <typename T> void foo (Vec<T>::type&);

Jadi, sintaksnya agak jelek. Kami lebih suka menghindari bersarang. ::type Kami lebih suka sesuatu seperti berikut ini:

template <typename T>
using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below
Vec<int> p; // sample usage

Perhatikan bahwa kami secara khusus menghindari istilah "template typedef" dan memperkenalkan sintaks baru yang melibatkan pasangan "menggunakan" dan "=" untuk membantu menghindari kebingungan: kami tidak mendefinisikan tipe apa pun di sini, kami memperkenalkan sinonim (yaitu alias) untuk sebuah abstraksi dari tipe-id (yaitu ekspresi tipe) yang melibatkan parameter template. Jika parameter template digunakan dalam konteks deducible dalam ekspresi tipe maka setiap kali alias template digunakan untuk membentuk template-id, nilai parameter template terkait dapat disimpulkan - lebih lanjut akan mengikuti. Dalam kasus apapun, sekarang dimungkinkan untuk menulis fungsi generik yang beroperasi Vec<T>dalam konteks deducible, dan sintaksnya juga ditingkatkan. Misalnya kita bisa menulis ulang foo sebagai:

template <typename T> void foo (Vec<T>&);

Kami menggarisbawahi di sini bahwa salah satu alasan utama untuk mengusulkan alias template adalah agar pengurangan argumen dan panggilan ke foo(p) akan berhasil.


Makalah tindak lanjut n1489 menjelaskan mengapa usingalih - alih menggunakan typedef:

Telah disarankan untuk menggunakan (kembali) kata kunci typedef - seperti yang dilakukan di makalah [4] - untuk memperkenalkan alias template:

template<class T> 
    typedef std::vector<T, MyAllocator<T> > Vec;

Notasi itu memiliki keuntungan menggunakan kata kunci yang sudah dikenal untuk memperkenalkan alias tipe. Namun, ini juga menampilkan beberapa kekurangan di antaranya kebingungan dalam menggunakan kata kunci yang dikenal untuk memperkenalkan alias untuk nama-tipe dalam konteks di mana alias tidak menunjuk pada suatu tipe, melainkan templat; Vecbukan sebuah alias untuk suatu tipe, dan tidak boleh diambil untuk nama-typedef. Nama Vecadalah nama untuk keluarga std::vector< [bullet] , MyAllocator< [bullet] > > - di mana poin adalah tempat penampung untuk nama tipe. Akibatnya kami tidak mengusulkan sintaks "typedef". Di sisi lain kalimat tersebut

template<class T>
    using Vec = std::vector<T, MyAllocator<T> >;

dapat dibaca / diartikan sebagai: mulai sekarang, saya akan menggunakan Vec<T>sebagai sinonim untuk std::vector<T, MyAllocator<T> >. Dengan pembacaan itu, sintaks baru untuk aliasing tampaknya masuk akal.

Saya pikir perbedaan penting dibuat di sini, alias es alih-alih tipe s. Kutipan lain dari dokumen yang sama:

Sebuah alias-deklarasi adalah sebuah deklarasi, dan bukan sebuah definisi. Sebuah alias- deklarasi memperkenalkan nama ke dalam kawasan deklaratif sebagai alias untuk tipe yang ditunjuk oleh sisi kanan deklarasi. Inti dari proposal ini berkaitan dengan alias nama tipe, tetapi notasi jelas dapat digeneralisasikan untuk memberikan ejaan alternatif dari namespace-aliasing atau set penamaan fungsi yang kelebihan beban (lihat ✁ 2.3 untuk diskusi lebih lanjut). [ Catatan saya: Bagian itu membahas seperti apa tampilan sintaks itu dan alasan mengapa itu bukan bagian dari proposal. ] Perlu dicatat bahwa deklarasi-alias produksi tata bahasa dapat diterima di mana pun deklarasi typedef atau definisi-alias-ruang nama dapat diterima.

Ringkasan, untuk peran using:

  • alias template (atau template typedefs, nama sebelumnya lebih disukai)
  • alias namespace (yaitu, namespace PO = boost::program_optionsdan using PO = ...setara)
  • kata dokumen itu A typedef declaration can be viewed as a special case of non-template alias-declaration. Ini adalah perubahan estetika, dan dianggap identik dalam kasus ini.
  • membawa sesuatu ke dalam ruang lingkup (misalnya, namespace stdke dalam lingkup global), fungsi anggota, mewarisi konstruktor

Ini tidak dapat digunakan untuk:

int i;
using r = i; // compile-error

Sebaliknya lakukan:

using r = decltype(i);

Memberi nama sekumpulan kelebihan beban.

// bring cos into scope
using std::cos;

// invalid syntax
using std::cos(double);

// not allowed, instead use Bjarne Stroustrup function pointer alias example
using test = std::cos(double);
imz - Ivan Zakharyaschev
sumber
2
@ user3111311 Apa kata kunci lain yang Anda pikirkan? "mobil"? "daftar"?
Raymond Chen
3
using P = [](double)->void;adalah, AFAIK, bukan C ++ 11 yang valid. Namun ini adalah: using P = auto(double)->void;dan menghasilkan tipe fungsi (seperti itu P*adalah penunjuk fungsi).
dyp
2
Namanya Bjarne Stroustrup;) (perhatikan r kedua di Stroustrup)
dyp
1
@RaymondChen: sebenarnya registertidak akan terdengar seburuk itu, ada di:register X as Y
MFH
1
Sayangnya registermemulai deklarasi variabel jadi ini sudah memiliki arti. Deklarasikan variabel register yang disebut Y tipe X.
Raymond Chen