Mengapa saya tidak bisa membuat vektor referensi?

351

Ketika saya melakukan ini:

std::vector<int> hello;

Semuanya bekerja dengan baik. Namun, ketika saya menjadikannya sebagai vektor referensi:

std::vector<int &> hello;

Saya mendapatkan kesalahan mengerikan seperti

kesalahan C2528: 'pointer': pointer ke referensi adalah ilegal

Saya ingin meletakkan banyak referensi untuk struct ke dalam vektor, sehingga saya tidak perlu ikut campur dengan pointer. Mengapa vektor membuat ulah tentang ini? Apakah satu-satunya pilihan saya untuk menggunakan vektor pointer?

Colen
sumber
46
Anda dapat menggunakan std :: vector <reference_wrapper <int>> hello; Lihat informit.com/guides/content.aspx?g=cplusplus&seqNum=217
amit
2
@amit tautannya tidak berlaku lagi, dokumentasi resmi di sini
Martin

Jawaban:

339

Jenis komponen wadah seperti vektor harus dialihkan . Referensi tidak dapat ditugaskan (Anda hanya dapat menginisialisasi sekali ketika mereka dideklarasikan, dan Anda tidak dapat membuatnya referensi lain nanti). Jenis lain yang tidak dapat ditentukan juga tidak diizinkan sebagai komponen wadah, misalnya vector<const int>tidak diizinkan.

berita baru
sumber
1
Apakah Anda mengatakan saya tidak dapat memiliki vektor vektor? (Saya yakin saya telah melakukan itu ...)
James Curran
8
Ya, std :: vector <std :: vector <int>> benar, std :: vector dapat digunakan.
Martin Cote
17
Memang, inilah alasan "sebenarnya". Kesalahan tentang T * menjadi tidak mungkin T adalah U & hanya efek samping dari persyaratan yang dilanggar bahwa T harus dapat ditetapkan. Jika vektor dapat secara tepat memeriksa parameter tipe, maka itu mungkin akan mengatakan "persyaratan dilanggar: T & tidak dapat ditugaskan"
Johannes Schaub - litb
2
Memeriksa konsep yang dapat ditentukan di boost.org/doc/libs/1_39_0/doc/html/Assignable.html semua operasi kecuali swap valid pada referensi.
amit
7
Ini tidak lagi benar. Sejak C ++ 11, satu-satunya persyaratan operasi-independen untuk elemen harus "Dihapus", dan referensi tidak. Lihat stackoverflow.com/questions/33144419/… .
laike9m
119

ya Anda bisa, mencari std::reference_wrapper, yang meniru referensi tetapi dapat ditugaskan dan juga dapat "diulang"

Ion Todirel
sumber
4
Apakah ada cara untuk menghubungi panggilan get()pertama ketika mencoba mengakses metode instance kelas di wrapper ini? Misalnya reference_wrapper<MyClass> my_ref(...); my_ref.get().doStuff();tidak seperti referensi.
timdiels
3
Bukankah ini bisa secara langsung dicampurkan ke Tipe itu sendiri dengan mengembalikan referensi?
WorldSEnder
3
Ya, tetapi itu membutuhkan konteks yang menyiratkan konversi mana yang diperlukan. Akses anggota tidak melakukan itu, karenanya diperlukan .get(). Yang diinginkan timdiels adalah operator.; lihat proposal / diskusi terbaru tentang itu.
underscore_d
31

Sesuai sifatnya, referensi hanya dapat ditetapkan pada saat mereka dibuat; yaitu, dua baris berikut memiliki efek yang sangat berbeda:

int & A = B;   // makes A an alias for B
A = C;         // assigns value of C to B.

Lebih lanjut, ini ilegal:

int & D;       // must be set to a int variable.

Namun, saat Anda membuat vektor, tidak ada cara untuk menetapkan nilai pada item-item itu saat dibuat. Anda pada dasarnya hanya membuat sejumlah contoh terakhir.

James Curran
sumber
10
"Ketika Anda membuat vektor, tidak ada cara untuk menetapkan nilai pada item-item itu pada pembuatan" Saya tidak mengerti apa yang Anda maksud dengan pernyataan ini. Apa itu "barang-barangnya saat pembuatan"? Saya dapat membuat vektor kosong. Dan saya dapat menambahkan item dengan .push_back (). Anda hanya menunjukkan bahwa referensi tidak dapat dibangun secara default. Tapi saya pasti dapat memiliki vektor kelas yang tidak dapat dibangun-standar.
newacct
2
Elemen ype dari std :: vector <T> tidak diperlukan untuk dapat dibangun secara default. Anda dapat menulis struct A {A (int); pribadi: A (); }; vektor <A> a; baik-baik saja - selama Anda tidak menggunakan metode seperti itu yang mengharuskannya untuk dibangun secara default (seperti v.resize (100); - tetapi Anda harus melakukan v.resize (100, A (1));)
Johannes Schaub - litb
Dan bagaimana Anda menulis push_back () dalam kasus ini? Masih akan menggunakan tugas, bukan konstruksi.
James Curran
3
James Curran, Tidak ada konstruksi default terjadi di sana. push_back hanya penempatan-berita A ke dalam buffer yang telah dialokasikan sebelumnya. Lihat di sini: stackoverflow.com/questions/672352/… . Perhatikan bahwa klaim saya hanya bahwa vektor dapat menangani jenis yang tidak dapat dibangun-standar. Saya tidak mengklaim, tentu saja, bahwa itu bisa menangani T & (tentu saja tidak bisa).
Johannes Schaub - litb
29

Ion Todirel sudah menyebutkan jawaban yang YA gunakan std::reference_wrapper. Karena C ++ 11 kami memiliki mekanisme untuk mengambil objek dari std::vector dan menghapus referensi dengan menggunakan std::remove_reference. Di bawah ini diberikan contoh dikompilasi menggunakan g++dan clangdengan opsi
-std=c++11dan berhasil dijalankan.

#include <iostream>
#include <vector>
#include<functional>

class MyClass {
public:
    void func() {
        std::cout << "I am func \n";
    }

    MyClass(int y) : x(y) {}

    int getval()
    {
        return x;
    }

private: 
        int x;
};

int main() {
    std::vector<std::reference_wrapper<MyClass>> vec;

    MyClass obj1(2);
    MyClass obj2(3);

    MyClass& obj_ref1 = std::ref(obj1);
    MyClass& obj_ref2 = obj2;

    vec.push_back(obj_ref1);
    vec.push_back(obj_ref2);

    for (auto obj3 : vec)
    {
        std::remove_reference<MyClass&>::type(obj3).func();      
        std::cout << std::remove_reference<MyClass&>::type(obj3).getval() << "\n";
    }             
}
Steephen
sumber
12
Saya tidak melihat nilainya di std::remove_reference<>sini. Intinya std::remove_reference<>adalah untuk memungkinkan Anda menulis "tipe T, tetapi tanpa menjadi referensi jika itu salah satu". Jadi std::remove_reference<MyClass&>::typesama saja dengan menulis MyClass.
alastair
2
Tidak ada nilai sama sekali dalam hal itu - Anda hanya dapat menulis for (MyClass obj3 : vec) std::cout << obj3.getval() << "\n"; (atau for (const MyClass& obj3: vec)jika Anda mendeklarasikan getval()const, sebagaimana mestinya).
Toby Speight
14

boost::ptr_vector<int> akan bekerja.

Sunting: adalah saran untuk digunakan std::vector< boost::ref<int> >, yang tidak akan berfungsi karena Anda tidak dapat membuat-default a boost::ref.

Drew Dormann
sumber
8
Tetapi Anda dapat memiliki jenis vektor atau non-konstruktif, kan? Anda hanya perlu berhati-hati untuk tidak menggunakan ctor default. dari vektor
Manuel
1
@Manuel: Atau resize.
Lightness Races di Orbit
5
Hati-hati, penunjuk penunjuk Boost mengambil kepemilikan eksklusif atas orang-orang yang ditunjuk. Kutipan : "Ketika Anda membutuhkan semantik yang dibagikan, perpustakaan ini bukan yang Anda butuhkan."
Matthäus Brandl
12

Ini cacat dalam bahasa C ++. Anda tidak dapat mengambil alamat referensi, karena mencoba melakukannya akan menghasilkan alamat objek yang dirujuk, dan dengan demikian Anda tidak akan pernah bisa mendapatkan pointer ke referensi. std::vectorbekerja dengan pointer ke elemen-elemennya, sehingga nilai-nilai yang disimpan harus dapat ditunjukkan. Anda harus menggunakan pointer sebagai gantinya.

Adam Rosenfield
sumber
Saya kira itu bisa diimplementasikan menggunakan void * buffer dan penempatan baru. Bukannya ini masuk akal.
peterchen
55
"Kelemahan dalam bahasa" terlalu kuat. Itu adalah dengan desain. Saya tidak berpikir bahwa vektor harus bekerja dengan pointer ke elemen. Namun elemen tersebut harus ditetapkan. Referensi tidak.
Brian Neal
Anda tidak dapat mengambil sizeofreferensi juga.
David Schwartz
1
Anda tidak memerlukan pointer ke referensi, Anda memiliki referensi, jika Anda membutuhkan pointer, simpan saja pointer itu sendiri; tidak ada masalah yang harus dipecahkan di sini
Ion Todirel
10

TL; DR

Gunakan std::reference_wrapperseperti ini:

#include <functional>
#include <string>
#include <vector>
#include <iostream>

int main()
{
    std::string hello = "Hello, ";
    std::string world = "everyone!";
    typedef std::vector<std::reference_wrapper<std::string>> vec_t;
    vec_t vec = {hello, world};
    vec[1].get() = "world!";
    std::cout << hello << world << std::endl;
    return 0;
}

Demo

Jawaban panjang

Seperti yang disarankan standar , untuk wadah standar yang Xberisi objek bertipe T, Tharus ErasabledariX .

Erasable berarti ungkapan berikut terbentuk dengan baik:

allocator_traits<A>::destroy(m, p)

Aadalah tipe pengalokasi penampung, madalah pengalokasi contoh dan pmerupakan penunjuk tipe *T. Lihat di sini untukErasable definisi.

Secara default, std::allocator<T>digunakan sebagai pengalokasi vektor. Dengan pengalokasi default, persyaratannya setara dengan validitas p->~T()(Perhatikan, Tini adalah tipe referensi dan ppenunjuk ke referensi). Namun, penunjuk ke suatu referensi adalah ilegal , oleh karena itu ungkapannya tidak terbentuk dengan baik.

ivaigult
sumber
3

Seperti yang telah disebutkan, Anda mungkin akan menggunakan vektor pointer saja.

Namun, Anda mungkin ingin mempertimbangkan menggunakan ptr_vector sebagai gantinya!

Martin Cote
sumber
4
Jawaban ini tidak bisa diterapkan, karena ptr_vector seharusnya merupakan penyimpanan. Itu akan menghapus pointer saat dihapus. Jadi itu tidak bisa digunakan untuk tujuannya.
Klemens Morgenstern
0

Seperti komentar lain menyarankan, Anda terbatas menggunakan pointer. Tetapi jika itu membantu, berikut adalah satu teknik untuk menghindari berhadapan langsung dengan petunjuk.

Anda dapat melakukan sesuatu seperti berikut ini:

vector<int*> iarray;
int default_item = 0; // for handling out-of-range exception

int& get_item_as_ref(unsigned int idx) {
   // handling out-of-range exception
   if(idx >= iarray.size()) 
      return default_item;
   return reinterpret_cast<int&>(*iarray[idx]);
}
Omid
sumber
reinterpret_casttidak diperlukan
Xeverous
1
Seperti yang ditunjukkan oleh jawaban lain, kami tidak terbatas menggunakan pointer sama sekali.
underscore_d