Neden referans vektörü yapamıyorum?


352

Bunu yaptığımda:

std::vector<int> hello;

Her şey harika çalışıyor. Ancak, bunun yerine bir referans vektörü yaptığınızda:

std::vector<int &> hello;

Gibi korkunç hatalar alıyorum

hata C2528: 'işaretçi': referans gösterici geçersiz

Ben işaretler ile karıştırmak zorunda kalmamak için, bir grup referansları bir vektör içine koymak istiyorum. Vektör neden bu konuda öfke nöbeti atıyor? Bunun yerine işaretçi vektörü kullanmak için tek seçeneğim var mı?


46
std :: vector <reference_wrapper <int>> merhaba kullanabilirsiniz; Bkz. İnformit.com/guides/content.aspx?g=cplusplus&seqNum=217
amit

2
@amit bağlantı artık geçerli değil, resmi belgeler burada
Martin

Yanıtlar:


340

Vektörler gibi kapların bileşen türü atanabilir olmalıdır . Referanslar atanamaz (yalnızca bir kez bildirildiklerinde başlatabilirsiniz ve daha sonra başka bir şeye referans veremezsiniz). Kapların bileşenleri olarak diğer atanamayan türlere de izin verilmez, örn vector<const int>.


1
Bir vektör vektörüne sahip olamayacağımı mı söylüyorsun? (Eminim bunu yaptım ...)
James Curran

8
Evet, std :: vector <std :: vector <int>> doğrudur, std :: vector atanabilir.
Martin Cote

17
Gerçekten de, bu "gerçek" nedendir. T * 'nin T imkansız olması hatası U'dur ve T'nin atanması gereken ihlal edilen gereksinimin sadece bir yan etkisidir. Vektör, type parametresini tam olarak kontrol edebilseydi, muhtemelen "ihlal edilen gereksinim: T & atanamaz" derdi
Johannes Schaub - litb

2
Boost.org/doc/libs/1_39_0/doc/html/Assignable.html adresinden atanabilir kavramı kontrol etme takas dışındaki tüm işlemler referanslarda geçerlidir.
amit

7
Bu artık doğru değil. C ++ 11 olduğundan, öğe için işlemden bağımsız tek gereksinim "Silinebilir" olmaktır ve başvuru değildir. Bkz. Stackoverflow.com/questions/33144419/… .
laike9m

119

evet, std::reference_wrapperbir referansı taklit edebilir, ancak atanabilir ve ayrıca "yeniden yerleştirilebilir"


4
get()Bu sarmalayıcıda bir sınıf örneğinin yöntemine erişmeye çalışırken ilk önce arama yapmanın bir yolu var mı ? Örneğin reference_wrapper<MyClass> my_ref(...); my_ref.get().doStuff();, çok referans değildir.
timdiels

3
Referansı döndürerek Türün kendisine kusursuz bir şekilde dökülebilir mi?
WorldSEnder

3
Evet, ancak bu, hangi dönüşümün gerekli olduğunu ima eden bir bağlam gerektirir. Üye erişimi bunu yapmaz, dolayısıyla ihtiyaç vardır .get(). Ne timdiels istiyor ise operator.; bununla ilgili en son tekliflere / tartışmalara bir göz atın.
underscore_d

32

Doğası gereği, referanslar ancak oluşturuldukları anda ayarlanabilir; yani, aşağıdaki iki satırın çok farklı etkileri vardır:

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

Dahası, bu yasadışı:

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

Ancak, bir vektör oluşturduğunuzda, oluşturma sırasında öğelerine değer atamanın bir yolu yoktur. Aslında son örnekten bir sürü yapıyorsunuz.


10
"Bir vektör oluşturduğunuzda, yaratılıştaki öğelerine değer atamanın bir yolu yoktur." Bu ifadeyle ne demek istediğinizi anlamıyorum. "Yaratılıştaki öğeleri" nedir? Boş bir vektör oluşturabilirim. Ve .push_back () ile öğeler ekleyebilirim. Sadece referansların varsayılan yapılamaz olduğuna dikkat çekiyorsunuz. Ama kesinlikle varsayılan olarak yapılandırılamayan sınıfların vektörlerine sahip olabilirim.
newacct

3
Std :: vector <T> öğesinin ype öğesinin varsayılan yapılandırılabilir olması gerekmez. A {A (int) yapısını yazabilirsiniz; özel: A (); }; vektör <A>; gayet iyi - varsayılan olarak yapılandırılabilir olmasını gerektiren yöntemleri kullanmadığınız sürece (v.resize (100) gibi; - ama bunun yerine v.resize (100, A (1));
Johannes Schaub - litb

Ve bu durumda nasıl bir push_back () yazarsınız? Hala inşaat değil, ödev kullanacaktır.
James Curran

4
James Curran, Orada varsayılan bir yapı yok. push_back sadece yerleştirme haber A önceden yerleştirilmiş arabellek içine. Buraya bakın: stackoverflow.com/questions/672352/… . Not o benim iddia vektör varsayılan olmayan-constructible türlerini işleyebilir sadece olmasıdır. Tabii ki T & 'yı kaldırabileceğini iddia etmiyorum (tabii ki yapamaz).
Johannes Schaub - litb

29

Ion Todirel zaten EVET kullanarak bir cevaptan bahsetti std::reference_wrapper. C ++ 11'den beri nesneyi std::vector kullanarak nesneyi almak ve referansı kaldırmak için bir mekanizmaya sahibiz std::remove_reference. Aşağıda g++ve clangseçeneği kullanılarak derlenen
-std=c++11ve başarıyla yürütülen bir örnek verilmiştir .

#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";
    }             
}

12
std::remove_reference<>Burada değeri görmüyorum . Buradaki nokta, std::remove_reference<>"T tipi, ancak eğer bir referans olmadan" yazmanıza izin vermektir. Yazmakla std::remove_reference<MyClass&>::typeaynı şey MyClass.
alastair

2
Hiç bir değeri yok - sadece yazabilirsiniz for (MyClass obj3 : vec) std::cout << obj3.getval() << "\n"; (ya for (const MyClass& obj3: vec)da getval()const beyan ederseniz, gerektiği gibi).
Toby Speight

14

boost::ptr_vector<int> çalışacak.

Edit:std::vector< boost::ref<int> > varsayılan bir yapı oluşturamadığınız için çalışmayacak bir öneriydi boost::ref.


8
Ancak bir vektör veya varsayılan olarak oluşturulamayan türleriniz olabilir, değil mi? Sadece varsayılan kralı kullanmamaya dikkat etmelisiniz. vektörü
Manuel

1
@Manuel: Veya resize.
Yörüngedeki Hafiflik Yarışları

5
Dikkatli olun, Boost'un işaretçi kapları pointe'lerin özel mülkiyetini alır. Alıntı : "Paylaşılan semantiğe ihtiyacınız olduğunda, bu kütüphane ihtiyacınız olan şey değildir."
Matthäus Brandl

12

C ++ dilinde bir kusur. Bir başvurunun adresini alamazsınız, çünkü bunu yapmaya çalışmak nesnenin adresinin başvurulmasına neden olur ve böylece hiçbir zaman bir referansa işaretçi alamazsınız. std::vectorelemanlarına işaretçilerle çalışır, bu nedenle saklanan değerlerin işaretlenmesi gerekir. Bunun yerine işaretçiler kullanmanız gerekir.


Ben bir geçersiz * tampon ve yeni yerleştirme kullanılarak uygulanabilir sanırım. Bu çok mantıklı değil.
peterchen

55
"Dilde kusur" çok güçlü. Tasarım gereğidir. Vektörün elemanlara işaretçilerle çalışması gerektiğini düşünmüyorum. Ancak, öğenin atanabilir olması gerekir. Referanslar değildir.
Brian Neal

sizeofReferansı da alamazsınız .
David Schwartz

1
bir referans için bir işaretçiye ihtiyacınız yok, referansa sahipsiniz, işaretçiye ihtiyacınız varsa, işaretçinin kendisini saklayın; Burada çözülmesi gereken bir sorun yok
Ion Todirel

10

TP; DR

Bunun std::reference_wrappergibi kullanın :

#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

Uzun cevap

Olarak standart göstermektedir , standart bir kap için Xtür nesneler içeren T, Tolmalıdır Erasableden X.

Erasable aşağıdaki ifadenin iyi oluşturulduğu anlamına gelir:

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

Akabın ayırıcı tipidir, mayırıcı örneğidir ve ptürün bir göstergesidir *T. Tanım için buraya bakın Erasable.

Varsayılan olarak, std::allocator<T>vektörün ayırıcısı olarak kullanılır. Varsayılan ayırıcı ile gereksinim, p->~T()( Tbir referans tipidir ve pbir referansın göstergesidir) geçerliliğine eşittir . Bununla birlikte, bir referansa işaret etmek yasa dışıdır , dolayısıyla ifade iyi biçimlendirilmemiştir.


3

Diğerlerinin de belirttiği gibi, bunun yerine muhtemelen bir işaretçi vektörü kullanacaksınız.

Ancak, bunun yerine bir ptr_vector kullanmayı düşünebilirsiniz !


4
Bir ptr_vector öğesinin bir depolama alanı olması gerektiğinden bu yanıt kullanılamaz. Yani kaldırma sırasında işaretçileri silecektir. Yani amacı için kullanılamaz.
Klemens Morgenstern

0

Diğer yorumların önerdiği gibi, işaretçiler kullanmakla sınırlısınız. Ancak yardımcı olursa, doğrudan işaretçilerle yüzleşmekten kaçınmak için bir teknik var.

Aşağıdaki gibi bir şey yapabilirsiniz:

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]);
}

reinterpret_castgerekli değil
Xeverous

1
Diğer cevapların da gösterdiği gibi, işaretçiler kullanmakla sınırlı değiliz.
underscore_d
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.