constexpr ใน for-Statement

c++17 ให้ if constexpr ซึ่ง:

ค่าของเงื่อนไขต้องเป็นนิพจน์คงที่ที่แปลงตามบริบทประเภท bool หากค่าเป็น true แสดงว่าคำสั่ง-false ถูกยกเลิก (ถ้ามี) มิฉะนั้น คำสั่ง-true จะถูกละทิ้ง

มีวิธีใช้สิ่งนี้ในคำสั่ง for ด้วยหรือไม่ หากต้องการคลายลูปในเวลาคอมไพล์? ฉันต้องการที่จะทำสิ่งนี้:

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

person Jonathan Mee    schedule 21.02.2018    source แหล่งที่มา
comment
ฉันไม่เห็น if constexpr ในรหัสของคุณ   -  person Jesper Juhl    schedule 21.02.2018
comment
@JesperJuhl ฮึขอบคุณ ฉันตั้งใจจะยกตัวอย่างว่าฉันต้องการใช้ for constexpr อย่างไร   -  person Jonathan Mee    schedule 21.02.2018
comment
stackoverflow.com/questions/45758545/ stackoverflow.com/questions/6872919/compile-time-loops คำตอบของคำถามเหล่านั้นช่วยคุณหรือ คุณต้องการใช้คำสั่ง for สำหรับการวนซ้ำอย่างชัดเจนหรือไม่?   -  person Sebi    schedule 21.02.2018
comment
ไม่ ไม่มี for constexpr ฉันเห็น การสนทนานี้ใน std -เสนอ Google Group   -  person Justin    schedule 21.02.2018


คำตอบ (3)


มีวิธีใช้สิ่งนี้ใน for-statement ด้วยหรือไม่? หากต้องการคลายลูปในเวลาคอมไพล์? ฉันอยากจะทำอะไรแบบนี้ได้บ้าง

ฉันไม่คิดด้วยวิธีง่ายๆ

แต่ถ้าคุณสามารถซื้อฟังก์ชันตัวช่วยได้ ด้วย std::integer_sequence และการเริ่มต้นของอาร์เรย์จำนวนเต็มสไตล์ C ที่ไม่ได้ใช้ โดยเริ่มจาก C++14 คุณสามารถทำสิ่งต่อไปนี้ได้

#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>();
 }

หากคุณสามารถใช้ C++17 คุณสามารถหลีกเลี่ยงอาร์เรย์ unused และเมื่อใช้การพับ foo_helper() สามารถเขียนได้ง่ายๆ ดังนี้

template <int ... Is>
void foo_helper (std::integer_sequence<int, Is...> const &)
 { ((std::cout << Is << std::endl), ...); }
person max66    schedule 21.02.2018
comment
คือว่า... ฉันไม่ได้มองหาวิธีที่จะปรับปรุงตัวอย่างของเล่นของฉันจริงๆ แต่ก็ดีที่มีน้ำใจ นี่เป็นเรื่องที่น่าสนใจ หากคุณสามารถอธิบายได้ว่า unused กำลังทำอะไรที่นี่ ก็คุ้มค่าที่จะได้ +1 จากฉัน - person Jonathan Mee; 21.02.2018
comment
@JonathanMee - ในความหมายฉันได้เพิ่มความเรียบง่าย C ++ 17 อย่างไรก็ตาม... unused เป็นเพียงอาร์เรย์สไตล์ C ที่ถูกประกาศว่าจะเริ่มต้นด้วยศูนย์เท่านั้น แต่การใช้กำลังของตัวดำเนินการลูกน้ำ คุณสามารถเพิ่มส่วน std::cout หน้า เครื่องหมายจุลภาคได้ เพื่อให้ดำเนินการ std::cout แต่ไม่ส่งผลต่อค่าของอาร์เรย์ กล่าวอีกนัยหนึ่ง unused เป็นเพียงกลอุบายในการเตรียมสภาพแวดล้อมที่จะคลายค่า Is... - person max66; 21.02.2018

หากคอมไพเลอร์ทราบขีดจำกัดการวนซ้ำ คอมไพเลอร์จะคลายการวนซ้ำ หากพบว่ามีประโยชน์ การคลายลูปทั้งหมดไม่ได้มีประโยชน์! และไม่น่าเป็นไปได้ที่คุณจะตัดสินใจได้ดีกว่าคอมไพเลอร์เกี่ยวกับประโยชน์ของการคลายลูป

ไม่จำเป็นต้องใช้ constexpr for (ตามที่คุณกล่าวไว้) เนื่องจาก constexpr if กำลังเปิดใช้งานคุณสมบัติ - คุณสามารถใส่โค้ดซึ่งจะทำให้โปรแกรมมีรูปแบบไม่ถูกต้องภายใน constexpr if สาขาเท็จ - มันไม่ใช่การปรับให้เหมาะสมอย่างแท้จริง

Constexpr for ในทางกลับกัน จะเป็นการปรับให้เหมาะสมอย่างแท้จริง (อย่างน้อยก็อย่างที่คุณอธิบาย ไม่นับกรณีขอบของการวนซ้ำที่ดำเนินการ 0 ครั้ง) และด้วยเหตุนี้จึงควรปล่อยให้กฎการปรับให้เหมาะสม 'ราวกับ' ดีกว่า

person SergeyA    schedule 21.02.2018

ไม่ได้โดยไม่มีรหัสตัวช่วย

#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)),...);
  };
}

นั่นคือประปาของเรา

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

ลูปเวลาคอมไพล์พร้อมค่าในแต่ละขั้นตอน

หรือเราสามารถซ่อนรายละเอียดได้:

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>{} );
    })
  );
}

แล้วเราจะได้:

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>{});

สิ่งนี้สามารถปรับปรุงเพิ่มเติมได้ด้วยตัวแปรตัวอักษรและเทมเพลตที่ผู้ใช้กำหนด:

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

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

โดยที่ parse_value คือฟังก์ชัน constexpr ที่รับลำดับของ char... และสร้างการแสดงจำนวนเต็มที่ไม่ได้ลงนาม

person Yakk - Adam Nevraumont    schedule 21.02.2018