Bisakah saya mengembalikan pipa sementara ke operasi jangkauan?

9

Misalkan saya memiliki generate_my_rangekelas yang memodelkan a range(khususnya, adalah regular). Lalu apakah kode berikut ini benar:

auto generate_my_range(int some_param) {    
  auto my_transform_op = [](const auto& x){ return do_sth(x); };
  return my_custom_rng_gen(some_param) | ranges::views::transform(my_transform_op);
}
auto cells = generate_my_range(10) | ranges::to<std::vector>;

Apakah my_custom_rng_gen(some_param)diambil dengan nilai oleh operator pipa (pertama), atau apakah saya memiliki referensi yang menggantung setelah saya meninggalkan generate_my_rangeruang lingkup?

Apakah akan sama dengan panggilan functionnal ranges::views::transform(my_custom_rng_gen(some_param),my_transform_op)?

Apakah itu benar jika saya menggunakan referensi lvalue? misalnya:

auto generate_my_range(int some_param) {
  auto my_transform_op = [](const auto& x){ return do_sth(x); };
  auto tmp_ref = my_custom_rng_gen(some_param);
  return tmp_ref | ranges::views::transform(my_transform_op);
}

Jika rentang diambil oleh nilai untuk operasi ini, lalu apa yang harus saya lakukan jika saya meneruskan referensi nilai rendah ke sebuah wadah? Haruskah saya menggunakan ranges::views::all(my_container)pola?

Bérenger
sumber
Apakah my_custom_rng_gen (some_param) sudah dibatasi? Apakah maksud Anda sesuatu seperti godbolt.org/z/aTF8RN tanpa take (5)?
Porsche9II
@ Porsche9II Ya ini kisaran terbatas. Katakanlah ini adalah sebuah wadah
Bérenger

Jawaban:

4

Di pustaka rentang ada dua jenis operasi:

  • pandangan yang malas dan membutuhkan wadah yang mendasarinya ada.
  • tindakan yang bersemangat, dan menghasilkan wadah baru sebagai hasilnya (atau memodifikasi yang sudah ada)

Tampilannya ringan. Anda memberikan nilai dan mengharuskan wadah yang mendasarinya tetap valid dan tidak berubah.

Dari dokumentasi rentang-v3

Tampilan adalah pembungkus ringan yang menyajikan tampilan urutan elemen yang mendasari dalam beberapa cara kustom tanpa bermutasi atau menyalinnya. Tampilan murah untuk dibuat dan disalin dan memiliki semantik referensi yang tidak memiliki.

dan:

Setiap operasi pada rentang yang mendasari yang membatalkan iterator atau sentinelnya juga akan membatalkan pandangan yang mengacu pada bagian mana pun dari rentang itu.

Penghancuran wadah yang mendasari jelas membatalkan semua iterator untuk itu.

Dalam kode Anda, Anda secara spesifik menggunakan tampilan - Anda menggunakan ranges::views::transform. Pipa itu hanyalah gula sintaksis untuk membuatnya mudah ditulis seperti apa adanya. Anda harus melihat hal terakhir dalam pipa untuk melihat apa yang Anda hasilkan - dalam kasus Anda, ini adalah pemandangan.

Jika tidak ada operator pipa, mungkin akan terlihat seperti ini:

ranges::views::transform(my_custom_rng_gen(some_param), my_transform_op)

jika ada beberapa transformasi yang terhubung dengan cara itu Anda bisa melihat bagaimana jeleknya itu akan terjadi.

Jadi, jika my_custom_rng_genmenghasilkan semacam wadah, yang Anda ubah dan kemudian kembali, wadah itu akan hancur dan Anda memiliki referensi yang menggantung dari pandangan Anda. Jika my_custom_rng_genada pandangan lain terhadap sebuah wadah yang hidup di luar lingkup ini, semuanya baik-baik saja.

Namun, kompiler harus dapat mengenali bahwa Anda menerapkan tampilan pada wadah sementara dan memukul Anda dengan kesalahan kompilasi.

Jika Anda ingin fungsi Anda mengembalikan rentang sebagai wadah, Anda harus secara eksplisit "mematerialisasikan" hasilnya. Untuk itu, gunakan ranges::tooperator dalam fungsi.


Pembaruan: Untuk lebih tegas tentang komentar Anda "di mana dokumentasi mengatakan bahwa penyusunan rentang / perpipaan membutuhkan dan menyimpan tampilan?"

Pipa hanyalah gula sintaksis untuk menghubungkan berbagai hal dalam ekspresi yang mudah dibaca. Bergantung pada bagaimana digunakan, itu mungkin atau tidak dapat mengembalikan tampilan. Itu tergantung pada argumen sisi kanan. Dalam kasus Anda itu adalah:

`<some range> | ranges::views::transform(...)`

Jadi ekspresi mengembalikan apa pun yang views::transformdikembalikan.

Sekarang, dengan membaca dokumentasi transformasi:

Di bawah ini adalah daftar combinator rentang malas, atau pandangan, yang menyediakan Range-v3, dan uraian singkat tentang bagaimana masing-masing dimaksudkan untuk digunakan.

[...]

views::transform

Dengan rentang sumber dan fungsi unary, kembalikan rentang baru di mana setiap elemen hasil adalah hasil penerapan fungsi unary ke elemen sumber.

Jadi ia mengembalikan suatu jangkauan, tetapi karena ia adalah operator yang malas, maka jangkauan yang dikembalikannya adalah sebuah tampilan, dengan semua semantiknya.

CygnusX1
sumber
Baik. Apa yang agak misterius bagi saya masih adalah bagaimana cara kerjanya ketika saya melewati wadah ke pipa (yaitu objek jangkauan yang dibuat oleh komposisi). Itu perlu menyimpan pandangan wadah entah bagaimana. Apakah sudah selesai dengan ranges::views::all(my_container)? Dan bagaimana jika pandangan dilewatkan ke pipa? Apakah itu mengakui itu lewat wadah atau pemandangan? Apakah perlu? Bagaimana?
Bérenger
"Kompilator harus dapat mengenali bahwa Anda menerapkan tampilan pada wadah sementara dan memukul Anda dengan kesalahan kompilasi" Itulah yang saya pikirkan juga: jika saya melakukan sesuatu yang bodoh, itu berarti kontrak pada tipe (menjadi kiri nilai) tidak terpenuhi. Hal-hal seperti itu dilakukan oleh range-v3. Tetapi dalam kasus ini sama sekali tidak ada masalah. Ini mengkompilasi DAN berjalan. Jadi mungkin ada perilaku yang tidak terdefinisi, tetapi tidak muncul.
Bérenger
Untuk memastikan apakah kode Anda berjalan dengan benar secara tidak sengaja atau jika semuanya baik-baik saja, saya perlu melihat isinya my_custom_rng_gen. Bagaimana tepatnya pipa dan transforminteraksi di bawah kap tidak penting. Seluruh ekspresi mengambil rentang sebagai argumen (wadah atau tampilan ke beberapa wadah) dan mengembalikan tampilan yang berbeda ke wadah itu. Nilai pengembalian tidak akan pernah memiliki wadah, karena itu adalah tampilan.
CygnusX1
1

Diambil dari dokumentasi rentang-v3 :

Tampilan [...] memiliki semantik referensi yang tidak memiliki.

dan

Memiliki objek rentang tunggal memungkinkan jaringan pipa operasi. Dalam sebuah pipa, suatu rentang diadaptasi dengan malas atau bermutasi dengan penuh semangat dalam beberapa cara, dengan hasilnya segera tersedia untuk adaptasi atau mutasi lebih lanjut. Adaptasi malas ditangani oleh pandangan, dan mutasi yang bersemangat ditangani oleh tindakan.

// taken directly from the the ranges documentation
std::vector<int> const vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
using namespace ranges;
auto rng = vi | views::remove_if([](int i){ return i % 2 == 1; })
              | views::transform([](int i){ return std::to_string(i); });
// rng == {"2","4","6","8","10"};

Dalam kode di atas, rng hanya menyimpan referensi ke data yang mendasarinya dan fungsi filter dan transformasi. Tidak ada pekerjaan yang dilakukan sampai rng diulang.

Karena Anda mengatakan bahwa rentang sementara dapat dianggap sebagai wadah, fungsi Anda mengembalikan referensi yang menggantung.

Dengan kata lain, Anda perlu memastikan bahwa rentang yang mendasari hidup lebih lama dari tampilan, atau Anda dalam masalah.

Rumburak
sumber
Ya, pandangan tidak memiliki, tetapi di mana dokumentasi mengatakan bahwa rentang pembuatan / perpipaan mengambil dan menyimpan tampilan? Mungkin saja (dan saya pikir, hal yang baik) untuk memiliki kebijakan berikut: simpan dengan nilai jika rentang diberikan oleh referensi nilai.
Bérenger
1
@ Bérenger Saya menambahkan sedikit lebih banyak dari dokumentasi rentang. Tetapi intinya adalah: Suatu pandangan adalah tidak memiliki . Tidak peduli apakah Anda memberikannya nilai.
Rumburak