const vs constexpr กับตัวแปร


303

มีความแตกต่างระหว่างคำจำกัดความต่อไปนี้หรือไม่?

const     double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;

หากไม่ต้องการรูปแบบใดใน C ++ 11



ทั้งสองเป็นค่าคงที่เวลารวบรวม แต่คุณสามารถทำ const_cast แรกและเขียนมัน แต่มันจะถูกปรับให้เหมาะสมโดยคอมไพเลอร์ใด ๆ เนื่องจากสิ่งนี้จะไม่ส่งผลกระทบต่อ "การอ่าน" เนื่องจากเกิดขึ้นในเวลาคอมไพล์
Bonita Montero

คำตอบ:


347

ฉันเชื่อว่ามีความแตกต่าง ลองเปลี่ยนชื่อพวกเขาเพื่อที่เราจะได้พูดคุยเกี่ยวกับพวกเขาได้ง่ายขึ้น:

const     double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;

ทั้งPI1และPI2คงที่ซึ่งหมายความว่าคุณไม่สามารถแก้ไขได้ แต่เพียง PI2เป็นค่าคงที่เวลารวบรวม มันจะต้องเริ่มต้นในเวลารวบรวม PI1อาจเริ่มต้นได้ในเวลารวบรวมหรือเวลาทำงาน นอกจากนี้สามารถใช้ได้เฉพาะ PI2ในบริบทที่ต้องการค่าคงที่เวลารวบรวม ตัวอย่างเช่น:

constexpr double PI3 = PI1;  // error

แต่:

constexpr double PI3 = PI2;  // ok

และ:

static_assert(PI1 == 3.141592653589793, "");  // error

แต่:

static_assert(PI2 == 3.141592653589793, "");  // ok

เป็นสิ่งที่คุณควรใช้? ใช้แล้วแต่ความต้องการของคุณ คุณต้องการให้แน่ใจว่าคุณมีค่าคงที่เวลารวบรวมที่สามารถใช้ในบริบทที่ต้องการค่าคงที่เวลารวบรวมได้หรือไม่? คุณต้องการที่จะเริ่มต้นได้ด้วยการคำนวณทำในเวลาทำงาน? เป็นต้น


60
คุณแน่ใจไหม? เนื่องจากการconst int N = 10; char a[N];ทำงานและขอบเขตของอาร์เรย์ต้องเป็นค่าคงที่เวลาคอมไพล์
fredoverflow

10
ฉันแน่ใจว่าเท่าที่ฉันเขียนไป (ทดสอบแต่ละรายการก่อนโพสต์) อย่างไรก็ตามคอมไพเลอร์ของฉันให้ฉันแปลงPI1เป็นค่าคงที่อินทิกรัลคอมไพล์เวลาสำหรับการใช้ในอาเรย์ แต่ไม่ใช่สำหรับใช้เป็นพารามิเตอร์เท็มเพลตแบบไม่มีชนิด ดังนั้นความสามารถในการคอมไพล์เวลาแปลงPI1เป็นประเภทอินทิกรัลดูเหมือนจะเป็นเรื่องฮิตและพลาดไปสำหรับฉัน
Howard Hinnant

34
@FredOverflow: ไม่ const ดัชนีอาร์เรย์ได้ "ทำงาน" ประมาณทศวรรษที่ผ่านมา (มีตัวอย่างเช่นกรัม ++ ส่วนขยายสำหรับที่) แต่ไม่ได้หมายความว่ามันถูกต้องตามกฎหมายอย่างเคร่งครัด c ++ (แม้ว่าบาง C เมื่อเร็ว ๆ นี้หรือ C ++ มาตรฐานทำให้มันถูกต้องตามกฎหมายผม ลืมอันไหน) สำหรับความแตกต่างในค่าคงที่คอมไพล์ไทม์พารามิเตอร์เทมเพลตและใช้เป็นenuminitializer เป็นเพียงความแตกต่างที่โดดเด่นเพียงสองอย่างระหว่างconstและconstexpr(และไม่สามารถใช้งานได้double)
Damon

17
ย่อหน้าที่ 4 ของ 5.19 การแสดงออกอย่างต่อเนื่อง [expr.const] เป็นหมายเหตุ (ไม่ใช่เชิงบรรทัดฐาน) ที่มีชื่อเสียงแสดงให้เห็นว่าการดำเนินการได้รับอนุญาตให้ทำเลขคณิตจุดลอยตัวที่แตกต่างกัน (เช่นเกี่ยวกับความถูกต้อง) ในเวลาคอมไพล์ ดังนั้น1 / PI1และ1 / PI2อาจให้ผลลัพธ์ที่แตกต่าง ฉันไม่คิดว่าเรื่องนี้เป็นเรื่องสำคัญพอ ๆ กับคำแนะนำในคำตอบนี้อย่างไรก็ตาม
Luc Danton

4
แต่มันconstexpr double PI3 = PI1;ทำงานได้อย่างถูกต้องสำหรับฉัน (MSVS2013 CTP) ผมทำอะไรผิดหรือเปล่า?
NuPagadi

77

ไม่มีความแตกต่างที่นี่ แต่มันสำคัญเมื่อคุณมีประเภทที่มีคอนสตรัคเตอร์

struct S {
    constexpr S(int);
};

const S s0(0);
constexpr S s1(1);

s0เป็นค่าคงที่ แต่ไม่ได้รับประกันว่าจะเริ่มต้นได้ในเวลาคอมไพล์ s1ถูกทำเครื่องหมายconstexprดังนั้นจึงเป็นค่าคงที่และเนื่องจากคอนSสตรัคเตอร์ของถูกทำเครื่องหมายconstexprด้วยมันจะถูกเริ่มต้นในเวลารวบรวม

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


3
ฉันเห็นด้วย: ข้อสรุปที่ฉันได้รับคือconstexprจะนำไปสู่การวินิจฉัยว่าการคำนวณเวลารวบรวมของวัตถุนั้นเป็นไปไม่ได้ สิ่งที่ชัดเจนน้อยกว่าคือฟังก์ชั่นที่คาดว่าพารามิเตอร์คงที่สามารถเรียกใช้งานในเวลาคอมไพล์ได้หรือไม่หากพารามิเตอร์นั้นถูกประกาศเป็นconstและไม่เป็นเช่นconstexpr: จะconstexpr int foo(S)ถูกดำเนินการในเวลารวบรวมถ้าฉันเรียกfoo(s0)?
Matthieu M.

4
@ MatthieuM: ฉันสงสัยว่าfoo(s0)จะถูกประหารชีวิตในเวลารวบรวม แต่คุณไม่เคยรู้: คอมไพเลอร์ได้รับอนุญาตให้ทำการเพิ่มประสิทธิภาพดังกล่าว แน่นอนค่า GCC 4.7.2 มิได้เสียงดังกราว 3.2 ให้ฉันไปรวบรวมconstexpr a = foo(s0);
RICI

50

constexprบ่งชี้ค่าที่คงที่และเป็นที่รู้จักในระหว่างการรวบรวม
constระบุค่าที่คงที่เท่านั้น ไม่จำเป็นต้องรู้ในระหว่างการรวบรวม

int sz;
constexpr auto arraySize1 = sz;    // error! sz's value unknown at compilation
std::array<int, sz> data1;         // error! same problem

constexpr auto arraySize2 = 10;    // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr

โปรดทราบว่า const ไม่ได้เสนอการรับประกันเช่นเดียวกับ constexpr เนื่องจากวัตถุ const ไม่จำเป็นต้องเริ่มต้นด้วยค่าที่ทราบในระหว่างการรวบรวม

int sz;
const auto arraySize = sz;       // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation

วัตถุ constexpr ทั้งหมดเป็น const แต่ไม่ใช่วัตถุ const ทั้งหมดที่เป็น constexpr

หากคุณต้องการให้คอมไพเลอร์รับประกันว่าตัวแปรมีค่าที่สามารถใช้ในบริบทที่ต้องการค่าคงที่เวลาคอมไพล์เครื่องมือในการเข้าถึงคือ constexpr ไม่ใช่ const


2
ฉันชอบคำอธิบายของคุณมาก .. คุณสามารถแสดงความคิดเห็นเพิ่มเติมเกี่ยวกับกรณีที่เราอาจจำเป็นต้องใช้ค่าคงที่เวลารวบรวมในสถานการณ์ชีวิตจริง
Mayukh Sarkar

1
@MayukhSarkar เพียงใช้ Google C ++ เพราะเหตุใด constexprเช่นstackoverflow.com/questions/4748083/…
underscore_d

18

constexprสัญลักษณ์คงต้องได้รับค่าที่เป็นที่รู้จักกันในเวลารวบรวม ตัวอย่างเช่น:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    constexpr int c2 = n+7;   // Error: we don’t know the value of c2
    // ...
}

เพื่อจัดการกับกรณีที่ค่าของ“ ตัวแปร” ที่เริ่มต้นด้วยค่าที่ไม่รู้จักในเวลารวบรวม แต่ไม่เคยเปลี่ยนแปลงหลังจากเริ่มต้น C ++ มีรูปแบบที่สองของค่าคงที่ ( const ) ตัวอย่างเช่น:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    const int c2 = n+7; // OK, but don’t try to change the value of c2
    // ...
    c2 = 7; // error: c2 is a const
}

“ ตัวแปรconst ” นั้นเป็นเรื่องธรรมดามากด้วยเหตุผลสองประการ:

  1. C ++ 98 ไม่ได้มี constexpr เพื่อให้คนใช้const
  2. ลิสต์รายการ“ ตัวแปร” ที่ไม่ใช่นิพจน์คงที่ (ไม่ทราบค่าของมันในเวลารวบรวม) แต่อย่าเปลี่ยนค่าหลังจากการเริ่มต้นมีประโยชน์อย่างกว้างขวาง

การอ้างอิง: "การเขียนโปรแกรม: หลักการและการฝึกใช้ C ++" โดย Stroustrup


25
บางทีคุณควรได้กล่าวว่าข้อความในคำตอบของคุณจะถูกนำคำต่อคำจาก "การเขียนโปรแกรม: หลักการและการปฏิบัติใช้ C ++" โดย Stroustrup
Aky
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.