Pertanyaan Mengapa kita bisa menggunakan `std :: move` pada objek` const`?


Di C ++ 11, kita dapat menulis kode ini:

struct Cat {
   Cat(){}
};

const Cat cat;
std::move(cat); //this is valid in C++11

ketika saya menelepon std::move, itu berarti saya ingin memindahkan objek, yaitu saya akan mengubah objek. Untuk memindahkan a const objek tidak masuk akal, jadi mengapa std::move tidak membatasi perilaku ini? Ini akan menjadi perangkap di masa depan, bukan?

Di sini perangkap berarti seperti Brandon yang disebutkan dalam komentar:

"Saya pikir dia berarti" perangkap "dia licik licik karena jika dia tidak   sadar, dia berakhir dengan salinan yang bukan apa yang dia inginkan. "

Dalam buku 'Effective Modern C ++' oleh Scott Meyers, dia memberi contoh:

class Annotation {
public:
    explicit Annotation(const std::string text)
     : value(std::move(text)) //here we want to call string(string&&),
                              //but because text is const, 
                              //the return type of std::move(text) is const std::string&&
                              //so we actually called string(const string&)
                              //it is a bug which is very hard to find out
private:
    std::string value;
};

Jika std::move dilarang beroperasi di const objek, kita bisa dengan mudah menemukan bug, bukan?


76
2018-02-18 22:22


asal


Jawaban:


struct strange {
  mutable size_t count = 0;
  strange( strange const&& o ):count(o.count) { o.count = 0; }
};

const strange s;
strange s2 = std::move(s);

di sini kita melihat penggunaan std::move pada suatu T const. Ini mengembalikan a T const&&. Kami memiliki konstruktor bergerak untuk strange yang persis seperti ini.

Dan itu disebut.

Sekarang, memang benar bahwa tipe aneh ini lebih langka daripada bug yang akan diperbaiki oleh proposal Anda.

Namun, di sisi lain, yang ada std::move berfungsi lebih baik dalam kode generik, di mana Anda tidak tahu apakah jenis yang Anda kerjakan adalah a T atau a T const.


40
2018-02-18 22:44



Ada trik di sini Anda menghadap, yaitu itu std::move(cat)  tidak benar-benar memindahkan apa pun. Itu hanya memberitahu compiler untuk mencoba untuk bergerak. Namun, karena kelas Anda tidak memiliki konstruktor yang menerima const CAT&&, melainkan akan menggunakan implisit const CAT& salin konstruktor, dan salin dengan aman. Tidak ada bahaya, tidak ada jebakan. Jika konstruktor salin dinonaktifkan karena alasan apa pun, Anda akan mendapatkan kesalahan kompilator.

struct CAT
{
   CAT(){}
   CAT(const CAT&) {std::cout << "COPY";}
   CAT(CAT&&) {std::cout << "MOVE";}
};

int main() {
    const CAT cat;
    CAT cat2 = std::move(cat);
}

cetakan COPYtidak MOVE.

http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f

Perhatikan bahwa bug dalam kode yang Anda sebutkan adalah a kinerja masalah, bukan stabilitas masalah, jadi bug seperti itu tidak akan menyebabkan crash, selamanya. Itu hanya akan menggunakan salinan yang lebih lambat. Selain itu, bug tersebut juga terjadi untuk objek non-const yang tidak memiliki konstruktor bergerak, jadi hanya menambahkan const kelebihan beban tidak akan menangkap semuanya. Kita bisa memeriksa kemampuan untuk memindahkan konstruk atau memindahkan assign dari tipe parameter, tetapi itu akan mengganggu kode template generik seharusnya untuk jatuh kembali pada konstruktor salinan. Dan heck, mungkin seseorang ingin dapat membangunnya const CAT&&, siapa saya untuk mengatakan dia tidak bisa?


81
2018-02-18 22:28



Salah satu alasan mengapa jawaban yang tersisa telah diabaikan sejauh ini adalah kemampuan untuk umum kode menjadi tangguh dalam menghadapi langkah. Misalnya, katakanlah saya ingin menulis fungsi umum yang memindahkan semua elemen dari satu jenis wadah untuk membuat jenis penampung lain dengan nilai yang sama:

template <class C1, class C2>
C1
move_each(C2&& c2)
{
    return C1(std::make_move_iterator(c2.begin()),
              std::make_move_iterator(c2.end()));
}

Keren, sekarang saya bisa membuat a vector<string> dari deque<string> dan masing-masing individu string akan dipindahkan dalam proses.

Tetapi bagaimana jika saya ingin pindah dari map?

int
main()
{
    std::map<int, std::string> m{{1, "one"}, {2, "two"}, {3, "three"}};
    auto v = move_each<std::vector<std::pair<int, std::string>>>(m);
    for (auto const& p : v)
        std::cout << "{" << p.first << ", " << p.second << "} ";
    std::cout << '\n';
}

Jika std::move bersikeras tidakconst argumen, instantiasi di atas move_each tidak dapat dikompilasi karena mencoba untuk memindahkan file const int(itu key_type dari map). Tapi kode ini tidak peduli jika tidak bisa bergerak key_type. Ia ingin memindahkan mapped_type (std::string) untuk alasan kinerja.

Ini untuk contoh ini, dan banyak contoh lain seperti itu dalam pengkodean generik itu std::move adalah permintaan untuk pindah, bukan permintaan untuk pindah.


16
2018-02-19 00:10



Saya memiliki kekhawatiran yang sama dengan OP.

std :: move tidak menggerakkan objek, tidak menjamin objek dapat dipindahkan. Lalu mengapa itu disebut pindah?

Saya pikir menjadi tidak bisa bergerak dapat menjadi salah satu dari dua skenario berikut:

1. Tipe yang bergerak adalah const.

Alasan kami memiliki kata kunci const dalam bahasa adalah bahwa kami ingin compiler untuk mencegah perubahan ke objek yang didefinisikan sebagai const. Diberikan contoh dalam buku karya Scott Meyers:

    class Annotation {
    public:
     explicit Annotation(const std::string text)
     : value(std::move(text)) // "move" text into value; this code
     { … } // doesn't do what it seems to!    
     …
    private:
     std::string value;
    };

Apa artinya secara harfiah? Pindahkan string const ke anggota nilai - setidaknya, itulah pemahaman saya sebelum saya membaca penjelasannya.

Jika bahasa itu bermaksud untuk tidak bergerak atau tidak menjamin gerakan berlaku ketika std :: move () dipanggil, maka itu secara harfiah menyesatkan ketika menggunakan kata bergerak.

Jika bahasa mendorong orang menggunakan std :: pindah untuk memiliki efisiensi yang lebih baik, itu harus mencegah perangkap seperti ini sedini mungkin, terutama untuk jenis kontradiksi literal yang jelas ini.

Saya setuju bahwa orang harus sadar bahwa pergerakan konstan adalah tidak mungkin, tetapi kewajiban ini seharusnya tidak menyiratkan bahwa kompilator dapat diam ketika kontradiksi yang nyata terjadi.

2. Objek tidak memiliki konstruktor bergerak

Secara pribadi, saya pikir ini adalah cerita terpisah dari kekhawatiran OP, seperti kata Chris Drew

@hvd Sepertinya itu sedikit bukan argumen untuk saya. Hanya karena saran OP tidak memperbaiki semua bug di dunia tidak selalu berarti itu adalah ide yang buruk (mungkin, tapi bukan karena alasan yang Anda berikan). - Chris Drew


1
2017-08-26 20:08