[[no_unique_address]] ve aynı türde iki üye değeri


16

Ben etrafında oynuyorum [[no_unique_address]]içinde c++20.

Cppreference örneğinde boş bir tür Emptyve tür varZ

struct Empty {}; // empty class

struct Z {
    char c;
    [[no_unique_address]] Empty e1, e2;
};

Görünüşe göre, boyutu Zen azından 2türleri e1ve e2aynı olduğu için olmalıdır.

Ancak, gerçekten Zboyutu ile istiyorum 1. Bu bana neyi Paketleme işi, düşünme var Emptyfarklı zorlar ekstra bir şablon parametresi ile bazı sarıcı sınıfında e1ve e2.

template <typename T, int i>
struct Wrapper : public T{};

struct Z1 {
    char c;
    [[no_unique_address]] Wrapper<Empty,1> e1;
    [[no_unique_address]] Wrapper<Empty,2> e2;
};

Maalesef sizeof(Z1)==2. Boyut Z1olarak bir tane yapmak için bir hile var mı ?

Bunu gcc version 9.2.1ve ile test ediyorumclang version 9.0.0


Başvurumda çok sayıda boş form var

template <typename T, typename S>
struct Empty{
    [[no_unique_address]] T t;
    [[no_unique_address]] S s;
};

Hangi boş tip ise Tve Saynı zamanda boş tipler ve farklı ise! Bu tip bile boş olmasını istiyorum Tve Saynı türleridir.


2
TKendisine şablon argümanları eklemeye ne dersiniz ? Bu farklı türler üretecektir. Şu anda her ikisinin Wrappermiras kalması Tsizi geri tutuyor ...
Max Langhof

@MaxLanghof Şablon argümanı ekleyerek ne demek istiyorsun T? Şu anda Tbir şablon argümanı.
tom

Devralma T.
Evg

@Evg burada fark etmez.
eerorika

2
Sadece 1'den büyük olduğu için boş bırakmaz
Deduplicator

Yanıtlar:


6

Hangi boş tip ise Tve Saynı zamanda boş tipler ve farklı ise! Bu tip bile boş olmasını istiyorum Tve Saynı türleridir.

Bunu elde edemezsiniz. Teknik olarak bile o bile boş olacağını garanti edemez, konuşma Tve Sfarklı boş türleridir. Unutmayın: no_unique_addressbir özelliktir; nesneleri gizleme yeteneği tamamen uygulamaya bağlıdır. Standartlar açısından boş nesnelerin boyutunu zorlayamazsınız.

C ++ 20 uygulamaları olgunlaştıkça, [[no_unique_address]]bunun genellikle boş temel optimizasyon kurallarına uyacağını varsaymalısınız . Yani, aynı türdeki iki nesne alt nesne olmadığı sürece, muhtemelen saklanmayı bekleyebilirsiniz. Ama bu noktada, bir çeşit pot-şans.

Özel durumuna gelince Tve Saynı tip olmanın, bu kesinlikle mümkün değildir. "No_unique_address" isminin sonuçlarına rağmen, gerçek şu ki, C ++ aynı türden nesnelere iki işaret verildiğinde, bu işaretçilerin aynı nesneyi göstermesini veya farklı adresleri olmasını gerektirir. Buna "benzersiz kimlik kuralı" diyorum ve no_unique_addressbunu etkilemiyor. Kaynaktan [intro.object] / 9 :

Bir başka içine yerleştirilmişse, ya da en azından bir tane sıfır büyüklükte bir subobject ise eğer bit alanlar aynı adrese sahip olabilir değil ömür örtüşen İki nesneler ve farklı türdedir ; aksi takdirde farklı adresleri vardır ve ayrık bayt depolama alanı işgal ederler.

Boş türlerin [[no_unique_address]]üyeleri sıfır boyutundadır, ancak aynı türe sahip olmak bunu imkansız hale getirir.

Gerçekten de, düşünmek, yuvalama yoluyla boş türü gizlemeye çalışmak benzersiz kimlik kuralını hala ihlal ediyor. Durumunuzu Wrapperve Z1durumunuzu düşünün . Bir Verilen z1bir örneğidir ki Z1, açıktır z1.e1ve z1.e2farklı türde farklı nesnelerdir. Ancak, z1.e1içinde z1.e2ya da tam tersi iç içe değildir . Ve onlar farklı türleri vardır, süre (Empty&)z1.e1ve (Empty&)z1.e2vardır değil farklı türleri. Ama farklı nesnelere işaret ediyorlar.

Ve benzersiz kimlik kuralıyla, farklı adresleri olmalıdır . Bu nedenle e1ve e2nominal olarak farklı türler olsa da, iç kısımları da aynı içeren nesnedeki diğer alt nesnelere karşı benzersiz kimliğe uymalıdır. Tekrarlı.

İstediğiniz şey, nasıl denediğinizden bağımsız olarak şu anda olduğu gibi C ++ 'da imkansızdır.


Büyük açıklama, çok teşekkürler!
tom

2

Bildiğim kadarıyla, her iki üyeye de sahip olmak istiyorsanız bu mümkün değildir. Ancak, tür aynı ve boş olduğunda uzmanlaşabilir ve üyelerden yalnızca birine sahip olabilirsiniz:

template <typename T, typename S, typename = void>
struct Empty{
    [[no_unique_address]] T t;
    [[no_unique_address]] S s;

    constexpr T& get_t() noexcept { return t; };
    constexpr S& get_s() noexcept { return s; };
};

template<typename TS>
struct Empty<TS, TS, typename std::enable_if_t<std::is_empty_v<TS>>>{
    [[no_unique_address]] TS ts;

    constexpr TS& get_t() noexcept { return ts; };
    constexpr TS& get_s() noexcept { return ts; };
};

Tabii ki, üyeleri kullanan programın geri kalanının sadece bir üyenin olduğu durumla başa çıkmak için değiştirilmesi gerekir. Bu durumda hangi üyenin kullanıldığı önemli değil - sonuçta, benzersiz bir adresi olmayan durumsuz bir nesnedir. Gösterilen üye işlevleri bunu basitleştirmelidir.

maalesef sizeof(Empty<Empty<A,A>,A>{})==2A tamamen boş bir yapıdır.

Boş çiftlerin özyinelemeli sıkıştırmasını desteklemek için daha fazla uzmanlık getirebilirsiniz:

template<class TS>
struct Empty<Empty<TS, TS>, TS, typename std::enable_if_t<std::is_empty_v<TS>>>{
    [[no_unique_address]] Empty<TS, TS> ts;

    constexpr Empty<TS, TS>& get_t() noexcept { return ts; };
    constexpr TS&            get_s() noexcept { return ts.get_s(); };
};

template<class TS>
struct Empty<TS, Empty<TS, TS>, typename std::enable_if_t<std::is_empty_v<TS>>>{
    [[no_unique_address]] Empty<TS, TS> ts;

    constexpr TS&            get_t() noexcept { return ts.get_t(); };
    constexpr Empty<TS, TS>& get_s() noexcept { return ts; };
};

Daha da fazlası, böyle bir şeyi sıkıştırmak için Empty<Empty<A, char>, A>.

template <typename T, typename S>
struct Empty<Empty<T, S>, S, typename std::enable_if_t<std::is_empty_v<S>>>{
     [[no_unique_address]] Empty<T, S> ts;

    constexpr Empty<T, S>& get_t() noexcept { return ts; };
    constexpr S&           get_s() noexcept { return ts.get_s(); };
};

template <typename T, typename S>
struct Empty<Empty<S, T>, S, typename std::enable_if_t<std::is_empty_v<S>>>{
     [[no_unique_address]] Empty<S, T> st;

    constexpr Empty<S, T>& get_t() noexcept { return st; };
    constexpr S&           get_s() noexcept { return st.get_t(); };
};


template <typename T, typename S>
struct Empty<T, Empty<T, S>, typename std::enable_if_t<std::is_empty_v<T>>>{
     [[no_unique_address]] Empty<T, S> ts;

    constexpr T&           get_t() noexcept { return ts.get_t(); };
    constexpr Empty<T, S>  get_s() noexcept { return ts; };
};

template <typename T, typename S>
struct Empty<T, Empty<S, T>, typename std::enable_if_t<std::is_empty_v<T>>>{
     [[no_unique_address]] Empty<S, T> st;

    constexpr T&           get_t() noexcept { return st.get_s(); };
    constexpr Empty<S, T>  get_s() noexcept { return st; };
};

Bu güzel, ama ne yazık ki tamamen boş bir yapı sizeof(Empty<Empty<A,A>,A>{})==2nerede A.
tom

Bir get_empty<T>işlev eklerdim. Ardından, get_empty<T>zaten orada çalışıyorsa sol veya sağda yeniden kullanabilirsiniz .
Yakk - Adam Nevraumont
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.