Soru Derleyici tarafından oluşturulan hareket ettiricinin davranışı nedir?


does std::is_move_constructible<T>::value == true Ima etmek T kullanılabilir bir hareket ettirici var mı? Eğer öyleyse, varsayılan davranış nedir?

Aşağıdaki durumu düşünün:

struct foo {
    int* ptr;
};

int main() {
    {       
        std::cout << std::is_move_constructible<foo>::value << '\n';
        foo f;
        f.ptr = (int*)12;
        foo f2(std::move(f));
        std::cout << f.ptr << ' ' << f2.ptr << '\n';
    }
    return 0;
}

ve çıktı:

1
0000000C 0000000C

Öyle düşünmüştüm f.ptr olmalı nullptr. Yani bu durumda,

  1. f2 taşındı mı?
  2. Öyleyse, rengin geçersiz kılınmaması gerekir mi?
  3. Bir sınıfın örneklerinin doğru bir şekilde inşa edilip edilmediğini (eski olanı geçersiz kılabilir) nasıl bilebilirim?

(VS11 kullanıyorum.)

Güncelleştirme

Taşımacı öğesinin varsayılan davranışı, kopya oluşturucusuyla aynıdır, doğru mu? Eğer doğruysa

  1. Her zaman bir hareket ctorunun taşınan nesnenin kaynaklarını çalmasını bekleriz, varsayılanı beklendiği gibi davranmaz, bu yüzden bir varsayılan hamle yapmanın ne anlamı var?
  2. Bir sınıfın özel bir hareket ediciye sahip olup olmadığını nasıl anlayabilirim (düzgün davranması garanti edilebilir)?

Öyle görünüyor foo f2(std::move(f)); Birini ilan ettiğimde kopya ctor çağırır, bkz:

struct foo {
    int* ptr;
    foo() {}
    foo(const foo& other) {
        std::cout << "copy constructed\n";
    }
};

int main() {
    {       
        std::cout << std::is_move_constructible<foo>::value << '\n';
        foo f;
        foo f2(std::move(f));
    }
    system("pause");
    return 0;
}

Şimdi çıktı:

1
copy constructed

Eğer foo bir hareket yapıcısı var, o zaman olmaz foo f2(std::move(f)) Bunu aramak?

Şimdi sorularım: Bir sınıfın bir hareket katsayısı olup olmadığını nasıl anlarsınız ve eğer bir tane varsa, bunu nasıl açıkça söyleyebilirim?

Yapmaya çalıştığım şey…

template<typename T, bool has_move_ctor>
struct MoveAux;

template<typename T>
struct MoveAux<T, true> {
    static void doMove(T* dest, T* src) {
        new(dest) T(std::move(*src)); //move ctor
    }
};

template<typename T>
struct MoveAux<T, false> {
    static void doMove(T* dest, T* src) {
        new(dest) T(*src); //copy ctor
        src->~T();
    }
};

template<typename T>
inline doMove(T* dest, T* src) {
    MoveAux<T,/*a trait*/>::doMove(dest, src);
}

Ben de düşündüm std::is_move_constructible<T>::value şablona geçilebilirken, şu anda bu özelliğin yalnızca T t(T()) geçerli bir ifadedir, arayabilir T::T(const T&). Şimdi varsayalım T özel bir sınıf, sonra yukarıdaki gibi davranmasını istediğiniz şablonları istiyorum:

  1. Bir hareket ctor bildirmiyorsam, o şablon yönteminin MoveAux<T,false>::doMove.
  2. Birini ilan edersem, çağrılara ihtiyacım var MoveAux<T,true>::doMove.

Bu işi yapmak mümkün mü?


29
2018-04-17 08:34


Menşei


@DavidSchwartz: "bir nesneyi geçtikten sonra move"Herhangi bir üyesine erişemeyebilirsiniz". Bir nesneden ayrıldıktan sonra herhangi bir üyesine erişemeyeceğiniz doğrudur. İlkel türden hareket etmenin etkisi değişmeden bırakılır. Kendilerini hareket etmenin etkisi - standart kütüphane sınıfları çoğunlukla nesnenin belirsiz fakat geçerli bir halde kaldığını ve genelde en azından Yıkıcının çalışması gerekiyor, bu da yıkıcı tarafından erişilen üyelerin mantıklı değerler tutması gerektiği anlamına geliyor. - Steve Jessop
@DavidSchwartz: "Taşımak için bir nesneyi geçtikten sonra, üyelerinden hiçbirine erişemeyebilirsiniz." Bu hiç de doğru değil. Yukarıdaki kod, C ++ belirtimiyle iyi tanımlanmış bir davranışa sahiptir. Taşıyıcı, işaretçiyi kopyalayacak ve böylece orijinal nesnenin durumu tanımlanacaktır. - Nicol Bolas


Cevaplar:


yapar std::is_move_constructible<T>::value == true ima ediyor ki T kullanılabilir bir hareket ettirici var mı?

Ya bir hareket ettirici veya bir kopya oluşturucu. Kopyalama yapısının işleyişinin, taĢıma taĢıması üzerine kurulu olan tüm gereklilikleri yerine getirdiğini ve biraz daha fazlasını unutmayın.

Standart olarak, MoveConstructible Nesne, ifadenin değerlendirildiği bir tanesidir:

T u = rv; 

markaları u değerine eşdeğer rv inşaattan önce; Devlet rv  sonra taşınmak belirtilmemiş. Ancak, belirtilmemiş olduğundan, bu durum devletin biriyle aynı olabileceği anlamına gelir. rv vardı önce Başka bir deyişle, u bir olabilir kopya arasında rv.

Aslında, Standart tanımlar CopyConstructible kavram olmak arıtma arasında MoveConstructible kavram (yani herşey CopyConstructible aynı zamanda MoveConstructibleama tersi değil).

eğer öyleyse, varsayılan davranış nedir?

Kesin olarak oluşturulan bir hareket yapıcının davranışı, oluşturulduğu türdeki veri elemanlarının üye-bilge hareketini gerçekleştirmektir.

C ++ 11 Standardının Paragraf 12.8 / 15'ine göre:

Sendikasız bir sınıf için örtülü olarak tanımlanan kopyala / taşı yapıcı Xbir gerçekleştirir üye olarak kopyala / taşı   üsleri ve üyeleri. [ Not: Eşit başlatıcıları gerilmesi ya da- Statik olmayan veri elemanlarının yok sayılması. Görmek   ayrıca 12.6.2'deki örnek. —Kend notu]

Dahası:

1 - nedir f2 taşındı mı?

Evet.

2 - eğer öyleyse, rengin geçersiz kılınmaması gerekir mi?

Hareket etmek Işaretçi kopyalamakla aynı şey. Öyleyse, geçersiz kılma yok, ne de devam etmeli. Taşınan nesneyi belirli bir durumda bırakan bir hareket edicisi istiyorsanız (örn. nullptr), kendi sorumluluğunuzu yazmanız veya bu sorumluluğu gibi bazı akıllı işaretçi sınıflarına devretmeniz gerekir. std::unique_ptr.

Dikkat, bu kelime "geçersiz kılınan"Burada tam olarak doğru değil. Yapıcılar (taşınan işleçlerin yanı sıra) taşımaları, taşınan nesneyi geçerli (henüz belirtilmemiş) durum.

Başka bir deyişle, sınıf değişmezine saygı gösterilmeli - ve devlet üzerinde herhangi bir önkoşul olmayan (genellikle imha ve tahsis) hareket ettirilmiş nesneler üzerinde harekete geçmek mümkün olmalıdır.


20
2018-04-17 08:38



std::is_move_constructible<T>::value == true bir hareket edicinin varlığını ima etmez. - Howard Hinnant
@HowardHinnant: Düzenlendi, teşekkürler. - Andy Prowl
Kendi hareket edicinizi yazmaktan daha iyi: std :: unique_ptr kullanın. - Sebastian Redl
@SebastianRedl: Tabii ki. Ancak OP, hareket inşasının temellerini anlamaya çalışıyor ve ona şöyle diyor: “Sizin için anlambilimine dikkat eden bir şey kullanın"Tasarım açısından doğru olduğu kadarıyla, sorularına içten bir cevap olmaz." - Andy Prowl
@AndyProwl: "Kendi yazman gerekiyor" diye itiraz ediyorum. Cevap bağlamında "derleyici tarafından oluşturulan hareket kurucusu, sizin için ham işaretçiler için yapmaz" şeklinde yorumlanabilir, ayrıca "başka yol yoktur" şeklinde de yorumlanabilir. - Sebastian Redl


std :: is_move_constructible :: value == true, T'nin kullanılabilir bir hareket edicisine sahip olduğunu ima eder?

Hayır. Yapabileceğinizi belirtir. nesne tipinin bir rvalue ifadesini almak ve ondan bir nesne oluşturmak. Bunun hareket yapıcıyı mı yoksa kopya yapıcıyı mı kullandığı bu özellikle ilgili değil.

f2 hareket inşa edilir?

Evet.

eğer öyleyse, rengin geçersiz kılınmaması gerekir mi?

Hayır. Bu hareketin nasıl işlediği değil.

Bir sınıfın örneklerinin doğru bir şekilde inşa edilip edilmediğini (eski olanı geçersiz kılabilir) nasıl bilebilirim?

Bu, var olan "doğru olarak hareket ettirilmiş" kavramının tanımı değildir. "Eski olanı geçersiz kılmak" istiyorsan, bunu kendin yapmalısın.

Taşınma genellikle garantileri garanti eder hiçbir şey değil eski nesnenin durumu hakkında. Geçerli fakat tanımlanmamış bir durumda olacaktır. Bu durum çok daha “önce olduğu gibi” olabilir. İşaretçiyi işaretlemek, işaretçiyi kopyalamakla aynı şeydir.

Hareket ettikten sonra "geçerliliğini kaybetmek" istiyorsanız, bunu açıkça yapan kendi hareket oluşturucunuzu yazmanız gerekir.

(VS11 kullanıyorum)

Öyleyse derleyici tarafından oluşturulmuş hareket yapıcılarınız yok. hiç. İşaretçiler için hareket ve kopya yapıcıların her ikisi de aynı şeyi yaptıkları için önemli değil.


5
2018-04-17 08:38



T'nin özel bir taşıma kurucusunun olup olmadığını nasıl bilebilirim? will (! std :: is_trivially_move_constructible <T> :: değer && std :: is_move_constructible <T> :: değer) hile yapmak? - Frahm
@Frahm: Bu işe yaramaz, önemsiz olmayan bir kopya kurucusu olan bir nesne de önemsiz bir şekilde hareket edemez. Bir kullanıcı tarafından sağlanan hareket kurucusunun açık bir şekilde tedarik edilmesini belirleyen bir özellik yoktur. Ayrıca, neden hiç yaparsın bakım? Kullanıcı tarafından sağlanan veya değil, sadece Bunu aramak. - Nicol Bolas
T'nin uygun bir şekilde taşınabileceğini gösteren bir özellik için özel bir sınıf yazmak istiyorum, eğer olmasa da, eğer T (bekletme kaynakları) bir kopyalanmış kopyaya sahipse ve bir derleyici ise; oluşturulan hareket ctor, daha sonra bu yönteme çağrı beklendiği gibi davranmayacak. - Frahm
@Frahm: Bu senin hayali senin eklenen hükümlerle "düzgünce taşındı" mı yoksa gerçek "doğru şekilde taşındı"? Kullanıcı size hareket etmeyi düşündüğünüz bir tür verirse ve hareketin doğru bir şekilde harekete geçmemesi nedeniyle hareket etmiyorsa, bu sizin hatanız değildir. Bu yüzden, her zaman nesneyi hareket ettirerek kodunuzu uygun şekilde uygulayın. Ayrıca, kullanıcı tarafından sağlanan bir hareket yapıcıyı tespit edebilseydiniz bile, uygulanan doğru şekilde. - Nicol Bolas


Visual Studio 2012 / VC ++ 11'in çalıştığını unutmayın. değil derleyici oluşturulmuş hareket yapıcıları destekler; Aslında, bu teklifi "C ++ 11 Visual C ++ 11 Özellikleri" blog yazısı (vurgu benim):

Rvalue referansları v3.0, otomatik olarak harekete geçmek için yeni kurallar ekler   belirli koşullar altında kurucular ve hareket atama operatörleri.   Bu VC11'de uygulanmayacak, takip etmeye devam edecek   VC10'un davranışı asla otomatik olarak hareket oluşturmuyor   yapıcılar / hareket atama operatörleri.

İle ham işaretçilerTaşımacıları kendiniz tanımlamanız ve eski "taşınan" işaretçisini elle temizlemeniz gerekir:

class Foo 
{
public:

    // Move constructor
    Foo(Foo&& other)
        : m_ptr(other.m_ptr) // copy pointer value
    {
        // Clear out old "moved-from" pointer, to avoid dangling references
        other.m_ptr = nullptr;
    }

private:
    int* m_ptr;
};

Bunun yerine, akıllı işaretçi sevmek std::unique_ptr, taşıyıcının yerini doğru bir şekilde tanımlayın ve yalnızca arayabilirsiniz std::move:

class Foo 
{
public:

    // Move constructor
    Foo(Foo&& other)
        : m_ptr(std::move(other.m_ptr)) // move from other, 
                                        // old pointer automatically cleared
    {
    }

private:
    std::unique_ptr<int> m_ptr;
};

İle otomatik olarak oluşturuldu kurucuları hareket ettirirseniz, özel bir hareket oluşturucu tanımlamak zorunda değilsiniz üye bilge hareket senin için uygun.


2
2018-04-17 08:54





Taşımacının varsayılan davranışı kopya olarak aynıdır   kurucu, bu doğru mu? eğer doğruysa

Hayır. Yanlış. Sadece ilkel için doğrudur. Kopya kurucusununkine benzer.

Varsayılan oluşturulan kopya kurucusu kopya beyan edilen siparişte tüm üyelerin kurucusu

Ama varsayılan oluşturulan hareket kurucusu hareket beyan edilen siparişte tüm üyelerin kurucusu

Şimdi sıradaki soru, ilkellerin kopyalama / taşıma kurucusudur. ints floats pointerYapıyor musun?

Ans: Sadece değerleri kopyala (hem kopyala hem de taşı yap)


2
2018-04-17 13:08





n3376 12.8 / 15

Kesin olarak tanımlanmış kopya /yapıcıyı taşı sendika dışı bir sınıf X için bir memberwise kopyala /hareket üsleri ve üyeleri.

Her baz veya statik olmayan veriler üye, türüne uygun şekilde kopyalanır / taşınır:

- üye bir dizi ise, her eleman x'in karşılık gelen alt nesnesiyle doğrudan başlatılır;

- Bir üye m, referans tipi T && referans değerine sahipse, static_cast (x.m) ile doğrudan başlatılır;

- aksi takdirde, taban veya üye, ilgili taban veya x üyesi ile doğrudan başlatılır.


0
2018-04-17 08:39





Eğer foo bir hareket yapıcıya sahipse, o zaman f2 (std :: move (f)) 'i çağırmaz mı?   Kopya oluşturucunuzu sağladığınızda varsayılan taşıma kurucusunu almazsınız. Bunu almak için aşağıdaki satırı ekleyin (ve değişikliğe dikkat edin).   foo (foo && ifm) = varsayılan;


0
2017-11-29 19:41