มันจะไม่ดีที่จะทำเครื่องหมาย constexpr ฟังก์ชั่น C ++?


26

รับฟังก์ชั่นที่น่ารำคาญมาก

int transform(int val) {
    return (val + 7) / 8;
}

มันควรจะชัดเจนว่ามันง่ายที่จะเปลี่ยนฟังก์ชั่นนี้เป็นconstexprฟังก์ชั่นทำให้ฉันใช้มันเมื่อกำหนดconstexprตัวแปรเช่น:

constexpr int transform(int val) {
    return (val + 7) / 8;
}

ข้อสันนิษฐานของฉันคือสิ่งนี้เป็นการปรับปรุงอย่างเข้มงวดเนื่องจากฟังก์ชันยังสามารถเรียกใช้ในconstexprบริบทที่ไม่ได้และตอนนี้สามารถใช้เพื่อช่วยกำหนดตัวแปรค่าคงที่เวลาคอมไพล์ได้

คำถามของฉันคือมีสถานการณ์ที่นี่เป็นความคิดที่ไม่ดีหรือไม่? เช่นโดยการใช้ฟังก์ชั่นนี้constexprฉันจะเคยพบกับสถานการณ์ที่ฟังก์ชั่นนี้จะไม่สามารถใช้งานได้ในบางสถานการณ์หรือมันจะทำงานผิดปกติหรือไม่?


1
สิ่งเดียวที่ฉันคิดได้ก็คือคอมไพล์บั๊ก อาจเป็นไปได้ว่าการเรียกใช้ฟังก์ชัน constexpr แบบเรียกซ้ำอาจทำให้ขั้นตอนการคอมไพล์ช้ามากหรือแม้แต่คอมไพเลอร์ที่เกิดจากหน่วยความจำล่ม
Zan Lynx

คำตอบ:


19

สิ่งนี้สำคัญเฉพาะถ้าฟังก์ชั่นเป็นส่วนหนึ่งของส่วนต่อประสานสาธารณะและคุณต้องการรักษาเวอร์ชันอนาคตของ API ของคุณที่รองรับไบนารี ในกรณีดังกล่าวคุณต้องคิดอย่างรอบคอบว่าคุณต้องการพัฒนา API ของคุณอย่างไรและที่ใดที่คุณต้องการจุดส่วนขยายสำหรับการเปลี่ยนแปลงในอนาคต

นั่นทำให้ผู้constexprคัดเลือกตัดสินใจการออกแบบที่ยกเลิกไม่ได้ คุณไม่สามารถลบตัวระบุนี้ได้หากไม่มีการเปลี่ยนแปลง API ของคุณที่เข้ากันไม่ได้ นอกจากนี้ยัง จำกัด วิธีการใช้งานฟังก์ชั่นเช่นคุณจะไม่สามารถทำการบันทึกใด ๆ ภายในฟังก์ชั่นนี้ ไม่ใช่ทุกฟังก์ชั่นเล็ก ๆ น้อย ๆ จะยังคงอยู่ในนิรันดร

นั่นหมายความว่าคุณควรใช้constexprสำหรับฟังก์ชั่นที่มีฟังก์ชั่นที่บริสุทธิ์อย่างแท้จริงและที่จะเป็นประโยชน์ในเวลารวบรวม (เช่นสำหรับแม่แบบ metaprogramming) มันจะไม่ดีที่จะทำให้ฟังก์ชั่น constexpr เพียงเพราะการใช้งานในปัจจุบันเกิดขึ้นเป็น constexpr- ได้

ในกรณีที่ไม่จำเป็นต้องทำการประเมินเวลาแบบคอมไพล์การใช้ฟังก์ชั่นอินไลน์หรือฟังก์ชั่นที่มีการเชื่อมโยงภายในดูเหมือนจะเหมาะสมconstexprกว่า ตัวแปรทั้งหมดเหล่านี้มีเหมือนกันว่าส่วนของฟังก์ชั่นนั้น“ สาธารณะ” และมีอยู่ในหน่วยการคอมไพล์เดียวกันกับที่ตั้งการโทร

หากฟังก์ชั่นดังกล่าวไม่ได้เป็นส่วนหนึ่งของ API สาธารณะที่เสถียรนี่เป็นปัญหาน้อยเนื่องจากคุณสามารถเปลี่ยนการออกแบบตามใจชอบได้ แต่เนื่องจากตอนนี้คุณสามารถควบคุมไซต์การโทรได้ทั้งหมดจึงไม่จำเป็นต้องทำเครื่องหมายฟังก์ชั่น“ ในกรณี” คุณรู้ว่าคุณกำลังใช้ฟังก์ชันนี้ในบริบทของ constexpr หรือไม่ การเพิ่มตัวระบุที่ จำกัด โดยไม่จำเป็นอาจถูกพิจารณาว่าเป็นการทำให้งงงวย


12

การทำเครื่องหมายฟังก์ชั่นconstexprยังทำให้มันเป็นฟังก์ชั่นอินไลน์§ [dcl.constexpr] / 1:

ฟังก์ชั่นหรือสมาชิกข้อมูลคงที่ประกาศด้วยตัวระบุ constexpr เป็นฟังก์ชั่นแบบอินไลน์หรือตัวแปร (7.1.6)

inlineในทางกลับกันหมายความว่าคุณต้องมีคำจำกัดความของฟังก์ชั่นนั้นในทุกหน่วยการแปลที่อาจใช้ นั่นหมายถึงconstexprฟังก์ชั่นจะต้องเป็นอย่างใดอย่างหนึ่ง:

  1. จำกัด ให้ใช้ในหนึ่งหน่วยการแปลหรือ
  2. กำหนดไว้ในส่วนหัว

ฟังก์ชั่นทั่วไปส่วนใหญ่ที่คุณต้องการประกาศในส่วนหัวและกำหนดในไฟล์ต้นฉบับ (และสิ่งอื่น ๆ ที่ใช้พวกเขาเพียงแค่รวมส่วนหัวแล้วเชื่อมโยงกับไฟล์วัตถุของแหล่งที่มา) constexprก็จะไม่ทำงาน

ในทางทฤษฎีแล้วฉันคิดว่าคุณสามารถย้ายทุกอย่างไปยังส่วนหัวและมีไฟล์ต้นฉบับเพียงไฟล์เดียวที่รวมส่วนหัวทั้งหมด แต่สิ่งนี้อาจทำให้เกิดการคอมไพล์ครั้งมากและสำหรับโครงการที่ร้ายแรงที่สุดจะต้องใช้หน่วยความจำจำนวนมาก

constexprฟังก์ชั่นยังถูก จำกัด ในบางวิธีดังนั้นสำหรับฟังก์ชั่นบางอย่างมันอาจจะไม่ได้เป็นตัวเลือกได้เลย ข้อ จำกัด รวมถึง:

  1. ฟังก์ชั่นเสมือนไม่สามารถเป็นconstexprได้
  2. ประเภทที่ส่งคืนจะต้องเป็น 'ประเภทตัวอักษร "(เช่นไม่มีวัตถุที่มี ctors หรือ ttors ที่ไม่ใช่ trival)
  3. พารามิเตอร์ทั้งหมดจะต้องเป็นประเภทตามตัวอักษร
  4. ฟังก์ชั่นร่างกายไม่สามารถมีtryบล็อก
  5. ไม่สามารถมีคำจำกัดความผันแปรของชนิดที่ไม่ใช่ตัวอักษรหรือสิ่งใดก็ตามที่มีระยะเวลาคงที่หรือแบบเธรด

ฉันข้ามไปสองสามอย่างค่อนข้างคลุมเครือสิ่งต่าง ๆ (เช่นมันไม่สามารถมีคำสั่งgotoหรือasm) แต่คุณได้รับความคิด - สำหรับบางสิ่งบางอย่างมันไม่ทำงาน

บรรทัดล่าง: ใช่มีหลายสถานการณ์ที่จะเป็นความคิดที่ไม่ดี


"จะต้องไม่เป็นเสมือน (จนถึง C ++ 20)" ฉันสงสัยว่าฟังก์ชันเสมือนสามารถเป็น constexpr ได้อย่างไร คอมไพเลอร์ทำอะไร?
chaosink
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.