constexpr di untuk-Pernyataan

c++17 menyediakan if constexpr, yang di dalamnya:

nilai kondisi harus berupa ekspresi konstanta tipe bool yang dikonversi secara kontekstual. Jika nilainya true, maka pernyataan-salah dibuang (jika ada), jika tidak, pernyataan-benar dibuang

Apakah ada cara untuk menggunakan ini dalam pernyataan for juga? Untuk membuka gulungan loop pada waktu kompilasi? Saya ingin dapat melakukan sesuatu seperti ini:

template <int T>
void foo() {
   for constexpr (auto i = 0; i < T; ++i) cout << i << endl;
}

person Jonathan Mee    schedule 21.02.2018    source sumber
comment
Saya tidak melihat if constexpr di kode Anda..   -  person Jesper Juhl    schedule 21.02.2018
comment
@JesperJuhl Ugh, terima kasih, saya bermaksud memberikan contoh bagaimana saya ingin menggunakan for constexpr   -  person Jonathan Mee    schedule 21.02.2018
comment
stackoverflow.com/questions/45758545/ stackoverflow.com/questions/6872919/compile-time-loops Apakah jawaban pertanyaan tersebut membantu Anda, atau apakah Anda ingin menggunakan pernyataan for secara eksplisit untuk perulangan?   -  person Sebi    schedule 21.02.2018
comment
Tidak, tidak ada for constexpr. Saya melihat diskusi ini di std -proposal grup google   -  person Justin    schedule 21.02.2018


Jawaban (3)


Apakah ada cara untuk menggunakan ini dalam pernyataan for juga? Untuk membuka gulungan loop pada waktu kompilasi? Saya ingin bisa melakukan hal seperti ini

Saya tidak berpikir dengan cara yang sederhana.

Tetapi jika Anda mampu membeli fungsi pembantu, dengan std::integer_sequence dan inisialisasi array bilangan bulat gaya C yang tidak terpakai, mulai dari C++14 Anda dapat melakukan sesuatu sebagai berikut

#include <utility>
#include <iostream>

template <int ... Is>
void foo_helper (std::integer_sequence<int, Is...> const &)
 {
   using unused = int[];

   (void)unused { 0, (std::cout << Is << std::endl, 0)... };
 }

template <int T>
void foo ()
 { foo_helper(std::make_integer_sequence<int, T>{}); }

int main ()
 {
   foo<42>();
 }

Jika Anda dapat menggunakan C++17, Anda dapat menghindari array unused dan, dengan menggunakan pelipatan, foo_helper() dapat ditulis sebagai berikut

template <int ... Is>
void foo_helper (std::integer_sequence<int, Is...> const &)
 { ((std::cout << Is << std::endl), ...); }
person max66    schedule 21.02.2018
comment
Yah... Saya tidak benar-benar mencari cara untuk memperbaiki contoh mainan saya, tapi ya ampun, ini menarik. Jika Anda dapat menjelaskan apa yang unused lakukan di sini, ada baiknya saya memberi +1. - person Jonathan Mee; 21.02.2018
comment
@JonathanMee - Artinya saya telah menambahkan penyederhanaan C++17. Pokoknya... unused hanyalah array gaya C yang dideklarasikan hanya untuk diinisialisasi dengan nol. Namun dengan menggunakan kekuatan operator koma, Anda dapat menambahkan bagian std::cout sebelum koma sehingga std::cout dijalankan tetapi tidak mempengaruhi nilai array. Dengan kata lain, unused hanyalah sebuah trik untuk menyediakan lingkungan tempat membongkar nilai Is.... - person max66; 21.02.2018

Jika batas perulangan diketahui oleh kompiler, kompiler akan membuka gulungan perulangan, jika dirasa bermanfaat. Tidak semua pembukaan gulungan bermanfaat! Dan sepertinya Anda tidak akan membuat keputusan yang lebih baik daripada kompiler mengenai manfaat pembukaan loop.

Tidak diperlukan constexpr for (seperti yang Anda katakan), karena constexpr if mengaktifkan fitur - Anda dapat memasukkan kode yang akan membuat program salah bentuk di dalam constexpr if cabang palsu - ini bukan optimasi murni.

Constexpr for, di sisi lain, akan menjadi optimasi murni (setidaknya seperti yang Anda gambarkan, tidak termasuk kasus pinggiran loop yang dieksekusi 0 kali) dan oleh karena itu lebih baik diserahkan pada aturan optimasi 'seolah-olah'.

person SergeyA    schedule 21.02.2018

Bukan tanpa kode pembantu.

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  -> decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;

template<std::size_t...Is>
constexpr auto index_over( std::index_sequence<Is...> ) noexcept(true) {
  return [](auto&& f)
    RETURNS( decltype(f)(f)( index_t<Is>{}... ) );
}
template<std::size_t N>
constexpr auto index_upto( index_t<N> ={} ) noexcept(true) {
  return index_over( std::make_index_sequence<N>{} );
}

template<class F>
constexpr auto foreacher( F&& f ) {
  return [&f](auto&&...args) noexcept( noexcept(f(args))&&... ) {
    ((void)(f(args)),...);
  };
}

itu pipa ledeng kami.

template<int T>
void foo() {
  index_upto<T>()(
    foreacher([](auto I){
      std::cout << I << "\n";
    })
  );
}

loop waktu kompilasi dengan nilai pada setiap langkah.

Atau kita dapat menyembunyikan detailnya:

template<std::size_t start, std::size_t length, std::size_t step=1, class F>
constexpr void for_each( F&& f, index_t<start> ={}, index_t<length> ={}, index_t<step> ={} ) {
  index_upto<length/step>()(
    foreacher([&](auto I){
      f( index_t<(I*step)+start>{} );
    })
  );
}

maka kita akan mendapatkan:

for_each<0, T>([](auto I) {
  std::cout << I << "\n";
});

or

for_each([](auto I) {
  std::cout << I << "\n";
}, index_t<0>{}, index_t<T>{});

ini dapat ditingkatkan lebih lanjut dengan literal yang ditentukan pengguna dan variabel templat:

template<std::size_t I>
constexpr index_t<I> index{};

template<char...cs>
constexpr auto operator""_idx() {
  return index< parse_value(cs...) >;
}

di mana parse_value adalah fungsi constexpr yang mengambil urutan char... dan menghasilkan representasi bilangan bulat tak bertanda tangan.

person Yakk - Adam Nevraumont    schedule 21.02.2018