2012-05-21 24 views
24

Benim bir yapı güncelleştiriliyor ve bir std :: string üyesi eklemek istedim. Orijinal yapı şuna benzer: Bir normalde nesnenin önemsiz olmayan kurucular eklemeniz gerekir çünkü C++ 11 önemsiz üyeler ile anonim birliği

struct Value { 
    uint64_t lastUpdated; 

    union { 
    uint64_t ui; 
    int64_t i; 
    float f; 
    bool b; 
    }; 
}; 

Sadece birliğe bir std :: string üyenin eklenmesini

, tabii ki, bir derleme hatasına neden olur. Ancak, ben bir yapı içinde anonim birliği kullanıyorum

union U 
{ 
int a; 
int b; 
string s; 
U(); 
~U(); 
}; 

: In the case of std::string (text from informit.com)

Since std::string defines all of the six special member functions, U will have an implicitly deleted default constructor, copy constructor, copy assignment operator, move constructor, move assignment operator and destructor. Effectively, this means that you can't create instances of U unless you define some, or all of the special member functions explicitly.

Ardından web sitesi aşağıdaki örnek kodu vermek devam ediyor. Ben freenode ## C++ istedi ve bana bunun yerine yapı içinde yapıcı koymak oldu yapmanın doğru yolu anlattı ve bana bu örnek kod verdi:

#include <new> 

struct Point { 
    Point() {} 
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_; 
}; 

struct Foo 
{ 
    Foo() { new(&p) Point(); } 
    union { 
    int z; 
    double w; 
    Point p; 
    }; 
}; 

int main(void) 
{ 
} 

Ama oradan ben nasıl bilemiyorum std :: string'in tanımlanması gereken özel işlevlerin geri kalanını yapmak ve dahası, bu örnekte ctorun nasıl çalıştığına dair tam olarak net değilim.

Bunu bana açıklayabilecek birini bulabilir miyim?

+2

İhtiyacınız olan şey nedir? _really_ ihtiyacı uygun bir [varyant] (http://www.boost.org/libs/variant/) ... – ildjarn

+1

Zaten başka bir yerde kullanıyorum varyant sınıfım var . Bu durumda kullanmıyorum, çünkü bu bir ağdaki serileştirilmiş veriler içindir ve veriyi küçük tutmak istedim, bu yüzden bir Variant'ın her şeyi dışarıda tutmak istediğim sınıfa içsel (yazılı bilgi yazmak gibi) her şeyi tutar ve karar verir paket şemasına dayanan nedir? – OmnipotentEntity

cevap

21

Burada yeni yerleştirmeye gerek yoktur.

Varyant üyeleri derleyicinin oluşturduğu yapıcı tarafından başlatılamaz, ancak bir tanesini seçip ctor-initializer-list kullanarak başlatmanın zorluğu olmamalıdır. Anonim sendikalar içinde beyan edilen üyeler aslında içerik sınıfının üyeleridir ve içerik sınıfının yapıcısında başlatılabilir.

Bu davranış bölüm 9.5'de açıklanmıştır.[class.union]:

A ctor-initializer may initialize a variant member of the constructor’s class. If a ctor-initializer specifies more than one mem-initializer for the same member or for the same base class, the ctor-initializer is ill-formed.

Yani kod basitçe olabilir:

A union-like class is a union or a class that has an anonymous union as a direct member. A union-like class X has a set of variant members. If X is a union its variant members are the non-static data members; otherwise, its variant members are the non-static data members of all anonymous unions that are members of X .

ve bölüm 12.6.2 [class.base.init] yılında

bir vivifying zaman Elbette

#include <new> 

struct Point { 
    Point() {} 
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_; 
}; 

struct Foo 
{ 
    Foo() : p() {} // usual everyday initialization in the ctor-initializer 
    union { 
    int z; 
    double w; 
    Point p; 
    }; 
}; 

int main(void) 
{ 
} 

, yeni yerleşiminde kullanılmalıdır kurucuda başlatılandan başka varyant üye.

+0

Eğer 'Foo' yapıcısı tanımlanmışsa ancak bu üye varyantlarından birini (Foo(): p() {} ') yerine 'Foo() {}' yerine seçmezse ne olur? GCC 5.1 ve Clang 3.6, herhangi bir uyarı veya hata yapmadan kurucuyu derlemek için çıktılar: http://melpon.org/wandbox/permlink/gLkD49UOrrGhFUJc Ancak, standartın bu dava hakkında ne söylediğinin net değil. – dkim

+0

@dkim: Eminim, tüm varyant üyelerini aynı durumda, özellikle depolanan ancak başlatma işlemi gerçekleştirilmemiş halde bırakır. Bölüm 3.8 (Nesne Ömür Boyu), böyle bir durumda üyeler için izin verilen işlemleri belirtir. –

13

Bu new (&p) Point() örneğidir, Standart yerleşim new operatörüne (yeni bir ifade aracılığıyla) bir çağrıdır, bu nedenle neden <new>'u dahil etmeniz gerekir. Bu özel işleç, bellek ayırmadığı için yapması açısından özeldir, sadece kendisine ilettiğiniz şeyi döndürür (bu durumda &p parametresidir). İfadenin net sonucu, bir nesnenin inşa edilmiş olmasıdır. Eğer açık yıkıcı çağrılar ile bu sözdizimini birleştirirseniz

o zaman bir nesnenin ömrü boyunca tam kontrol elde edebilirsiniz:

// Let's assume storage_type is a type 
// that is appropriate for our purposes 
storage_type storage; 

std::string* p = new (&storage) std::string; 
// p now points to an std::string that resides in our storage 
// it was default constructed 

// *p can now be used like any other string 
*p = "foo"; 

// Needed to get around a quirk of the language 
using string_type = std::string; 

// We now explicitly destroy it: 
p->~string_type(); 
// Not possible: 
// p->~std::string(); 

// This did nothing to our storage however 
// We can even reuse it 
p = new (&storage) std::string("foo"); 

// Let's not forget to destroy our newest object 
p->~string_type(); 

ve inşa etmek ve std::string üyesini yok gereken yerde (en s diyelim) Value sınıfınızda, s kullanım deseninize bağlıdır. Bu minimal örnekte özel üyeler bunu inşa (ve dolayısıyla imha) asla:

Value v; 
new (&v.s) std::string("foo"); 
something_taking_a_string(v.s); 
using string_type = std::string; 
v.s.~string_type(); 

Fark ettiğin gibi, ben özürlü:

struct Value { 
    Value() {} 

    Value(Value const&) = delete; 
    Value& operator=(Value const&) = delete; 

    Value(Value&&) = delete; 
    Value& operator=(Value&&) = delete; 

    ~Value() {} 

    uint64_t lastUpdated; 

    union { 
     uint64_t ui; 
     int64_t i; 
     float f; 
     bool b; 
     std::string s; 
    }; 
}; 

aşağıdaki dolayısıyla Value geçerli bir kullanımıdır kopyalama ve taşıma Value. Bunun nedeni, hangisinin aktif olduğunu bilmeksizin, sendikanın uygun aktif üyesini kopyalayamamamız veya taşımamamızdır.

+5

* "// Dilin bir tuhaflığını bulmak için gerekli" * - aslında, bu yanlış - siz * doğru yapabilirseniz (doğrudan doğruya çözmeniz gerekiyorsa) doğrudan tahribatı arayabilirsin: 'p-> std :: dize :: ~ dizisi(); '. Yine de daha okunabilir mi? Eh, kesinlikle daha karmaşık görünüyor, ancak iyi bilinen bir veri türü kullanır, yukarıdaki çözüm daha kompakt ('' kullanmak için ek kod satırı dışında), ancak nadiren bilinen bir takma tanıttı. Kesinlikle kişisel bir zevk meselesi (kendimle ilgili olarak, iyi bilinen veri türü için oy kullanırdım ...). – Aconcagua