'const static' หมายถึงอะไรใน C และ C ++


117
const static int foo = 42;

ฉันเห็นสิ่งนี้ในโค้ดบางอย่างที่นี่ใน StackOverflow และฉันคิดไม่ออกว่ามันทำอะไร จากนั้นฉันก็เห็นคำตอบที่สับสนในฟอรัมอื่น ๆ ฉันเดาได้ดีที่สุดคือใช้ใน C เพื่อซ่อนค่าคงที่fooจากโมดูลอื่น ๆ ถูกต้องหรือไม่ ถ้าเป็นเช่นนั้นทำไมทุกคนถึงใช้มันในบริบท C ++ ซึ่งคุณสามารถทำได้private?

คำตอบ:


113

มีการใช้งานทั้ง C และ C ++

ตามที่คุณคาดเดาstaticส่วนนี้จะ จำกัด ขอบเขตไว้ที่หน่วยคอมไพล์นั้น นอกจากนี้ยังจัดเตรียมการเริ่มต้นแบบคงที่ constเพียงแค่บอกคอมไพเลอร์ว่าอย่าให้ใครแก้ไข ตัวแปรนี้ถูกใส่ในส่วนข้อมูลหรือ bss ขึ้นอยู่กับสถาปัตยกรรมและอาจอยู่ในหน่วยความจำที่ทำเครื่องหมายว่าอ่านอย่างเดียว

ทั้งหมดนี้คือวิธีที่ C ปฏิบัติต่อตัวแปรเหล่านี้ (หรือวิธีที่ C ++ ปฏิบัติต่อตัวแปรเนมสเปซ) ใน C ++ สมาชิกที่ทำเครื่องหมายstaticถูกแชร์โดยอินสแตนซ์ทั้งหมดของคลาสที่กำหนด ไม่ว่าจะเป็นแบบส่วนตัวหรือไม่ก็ตามจะไม่ส่งผลต่อการที่ตัวแปรหนึ่งตัวใช้ร่วมกันโดยหลายอินสแตนซ์ การมีconstอยู่จะเตือนคุณหากมีรหัสใดพยายามแก้ไข

หากเป็นแบบส่วนตัวอย่างเคร่งครัดอินสแตนซ์แต่ละคลาสจะได้รับเวอร์ชันของตัวเอง (อย่างไรก็ตามเครื่องมือเพิ่มประสิทธิภาพ)


1
ตัวอย่างเดิมกำลังพูดถึง "ตัวแปรส่วนตัว" ดังนั้นนี่คือ mebmer และคงไม่มีผลต่อการเชื่อมโยง คุณควรลบ "ส่วนคงที่ จำกัด ขอบเขตของไฟล์นั้น"
Richard Corden

"ส่วนพิเศษ" เรียกว่าส่วนข้อมูลซึ่งใช้ร่วมกับตัวแปรส่วนกลางอื่น ๆ ทั้งหมดเช่น "สตริง" ที่ชัดเจนและอาร์เรย์ส่วนกลาง ซึ่งตรงข้ามกับส่วนของโค้ด
spoulson

@ ริชาร์ด - อะไรทำให้คุณคิดว่าเป็นสมาชิกของชั้นเรียน? ไม่มีอะไรในคำถามที่ระบุว่าเป็น ถ้าเป็นสมาชิกของชั้นเรียนแสดงว่าคุณพูดถูก แต่ถ้าเป็นเพียงตัวแปรที่ประกาศในขอบเขตทั่วโลกคริสก็พูดถูก
Graeme Perrow

1
ผู้โพสต์ต้นฉบับกล่าวถึงความเป็นส่วนตัวว่าเป็นวิธีแก้ปัญหาที่ดีกว่า แต่ไม่ใช่ปัญหาเดิม
Chris Arguin

@Graeme ตกลงดังนั้นจึงไม่ใช่สมาชิกที่ "แน่นอน" - อย่างไรก็ตามคำตอบนี้เป็นการสร้างข้อความที่ใช้กับสมาชิกเนมสเปซเท่านั้นและข้อความเหล่านี้ไม่ถูกต้องสำหรับตัวแปรสมาชิก ด้วยจำนวนคะแนนที่ข้อผิดพลาดอาจทำให้คนที่ไม่คุ้นเคยกับภาษานี้สับสน - ควรได้รับการแก้ไข
Richard Corden

212

ผู้คนจำนวนมากให้คำตอบพื้นฐาน แต่ไม่มีใครชี้ให้เห็นว่าใน C ++ constค่าเริ่มต้นstaticที่namespaceระดับ (และบางส่วนให้ข้อมูลที่ไม่ถูกต้อง) ดูมาตรฐาน C ++ 98 ส่วน 3.5.3

ก่อนอื่นพื้นหลัง:

หน่วยการแปล:ซอร์สไฟล์หลังจากพรีโปรเซสเซอร์ (เรียกซ้ำ) รวมไฟล์ทั้งหมดที่รวมไว้

การเชื่อมโยงแบบคงที่:สัญลักษณ์จะใช้ได้เฉพาะในหน่วยการแปลเท่านั้น

การเชื่อมโยงภายนอก:มีสัญลักษณ์จากหน่วยการแปลอื่น ๆ

ในnamespaceระดับ

ซึ่งรวมถึง namespace โลก aka ตัวแปรทั่วโลก

static const int sci = 0; // sci is explicitly static
const int ci = 1;         // ci is implicitly static
extern const int eci = 2; // eci is explicitly extern
extern int ei = 3;        // ei is explicitly extern
int i = 4;                // i is implicitly extern
static int si = 5;        // si is explicitly static

ในระดับฟังก์ชัน

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

ในclassระดับ

staticหมายถึงค่าที่ใช้ร่วมกันระหว่างอินสแตนซ์ทั้งหมดของคลาสและconstหมายความว่าจะไม่เปลี่ยนแปลง


2
ที่ระดับฟังก์ชัน: คงที่ไม่ซ้ำซ้อนกับ const พวกมันสามารถทำงานได้แตกต่างกันconst int *foo(int x) {const int b=x;return &b};เมื่อเทียบกับconst int *foo(int x) {static const int b=x;return &b};
Hanczar

1
คำถามนี้เกี่ยวกับทั้ง C และ C ++ ดังนั้นคุณควรใส่หมายเหตุเกี่ยวกับการconstบอกเป็นนัยstaticในส่วนหลังเท่านั้น
Nikolai Ruhe

@Motti: คำตอบที่ดี คุณช่วยอธิบายได้ไหมว่าอะไรทำให้เกิดความซ้ำซ้อนในระดับฟังก์ชัน คุณกำลังบอกว่าconstการประกาศstaticมีนัยด้วยเช่นกัน? ในกรณีที่คุณทิ้งconstและแก้ไขค่าค่าทั้งหมดจะถูกแก้ไข?
คุกกี้

1
@Motti constไม่ได้หมายความแบบคงที่ในระดับฟังก์ชั่นที่จะเป็นฝันร้ายเห็นพ้องด้วย (const! = แสดงออกคงที่) autoทุกอย่างในระดับฟังก์ชั่นโดยปริยาย เนื่องจากคำถามนี้มีการติดแท็ก [c] ด้วยฉันจึงควรพูดถึงว่าระดับโลกconst intเป็นนัยexternใน C อย่างไรก็ตามกฎที่คุณมีในที่นี้อธิบาย C ++ ได้อย่างสมบูรณ์
Ryan Haining

1
และใน C ++ ในทั้งสามกรณีstaticบ่งชี้ว่าตัวแปรคือระยะเวลาคงที่ (มีเพียงสำเนาเดียวที่มีอยู่ซึ่งกินเวลาตั้งแต่จุดเริ่มต้นของโปรแกรมจนถึงจุดสิ้นสุด) และมีการเชื่อมโยงภายใน / แบบคงที่หากไม่ได้ระบุไว้เป็นอย่างอื่น (สิ่งนี้ถูกแทนที่โดยฟังก์ชันของ การเชื่อมโยงสำหรับตัวแปรคงท้องถิ่นหรือการเชื่อมโยงคลาสสำหรับสมาชิกแบบคงที่) ความแตกต่างที่สำคัญคือความหมายของสิ่งนี้ในแต่ละสถานการณ์ที่staticถูกต้อง
Justin Time - คืนสถานะ Monica

45

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

ขอบเขตเนมสเปซ

// foo.h
static const int i = 0;

' i' จะปรากฏในทุกหน่วยการแปลที่มีส่วนหัว แต่ถ้าคุณจริงใช้ที่อยู่ของวัตถุ (เช่น. ' &i') ผมค่อนข้างแน่ใจว่าคอมไพเลอร์จะปฏิบัติต่อ' i' 0เป็นเพียงความปลอดภัยประเภท ในกรณีที่หน่วยการแปลอีกสองหน่วยใช้เครื่องหมาย " &i" ที่อยู่จะแตกต่างกันไปสำหรับหน่วยการแปล

// foo.cc
static const int i = 0;

' i' มีการเชื่อมโยงภายในดังนั้นจึงไม่สามารถอ้างถึงจากภายนอกหน่วยการแปลนี้ได้ 0อย่างไรก็ตามอีกครั้งจนกว่าคุณจะใช้ที่อยู่ของมันส่วนใหญ่จะได้รับการปฏิบัติเป็นชนิดที่ปลอดภัย

สิ่งหนึ่งที่ควรชี้ให้เห็นก็คือคำประกาศต่อไปนี้:

const int i1 = 0;

คือว่าstatic const int i = 0เช่นเดียวกับ ตัวแปรในเนมสเปซที่ประกาศด้วยconstและไม่ได้ประกาศโดยชัดแจ้งexternเป็นแบบคงที่โดยปริยาย หากคุณคิดถึงเรื่องนี้เป็นความตั้งใจของคณะกรรมการ C ++ ที่อนุญาตให้constประกาศตัวแปรในไฟล์ส่วนหัวโดยไม่จำเป็นต้องใช้staticคีย์เวิร์ดเสมอไปเพื่อหลีกเลี่ยงการทำลาย ODR

ขอบเขตชั้นเรียน

class A {
public:
  static const int i = 0;
};

ในตัวอย่างข้างต้นมาตรฐานระบุอย่างชัดเจนว่า ' i' ไม่จำเป็นต้องกำหนดหากไม่ต้องการที่อยู่ กล่าวอีกนัยหนึ่งถ้าคุณใช้เฉพาะ ' i' เป็น type-safe 0 คอมไพเลอร์จะไม่กำหนด ความแตกต่างอย่างหนึ่งระหว่างคลาสและเวอร์ชันเนมสเปซคือที่อยู่ของ ' i' (ถ้าใช้ในหน่วยการแปลมากกว่าสองหน่วย) จะเหมือนกันสำหรับสมาชิกคลาส ในกรณีที่ใช้ที่อยู่คุณต้องมีคำจำกัดความสำหรับที่อยู่:

// a.h
class A {
public:
  static const int i = 0;
};

// a.cc
#include "a.h"
const int A::i;            // Definition so that we can take the address

2
+1 สำหรับชี้ให้เห็นว่า const คงที่เหมือนกับ const ในขอบเขตเนมสเปซ
Plumenator

จริงๆแล้วไม่มีความแตกต่างระหว่างการวางใน "foo.h" หรือใน "foo.cc" เนื่องจากมีการรวม. h เมื่อรวบรวมหน่วยการแปล
Mikhail

2
@ มิคาอิล: คุณถูกต้อง มีข้อสันนิษฐานว่าส่วนหัวสามารถรวมอยู่ใน TU หลาย ๆ ตัวได้ดังนั้นจึงเป็นประโยชน์ที่จะพูดถึงเรื่องนั้นแยกกัน
Richard Corden

24

เป็นการเพิ่มประสิทธิภาพพื้นที่ขนาดเล็ก

เมื่อคุณพูดว่า

const int foo = 42;

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

ภายนอก const int foo;

เพื่อเข้าถึงคุณค่าของมัน นั่นไม่ใช่แนวทางปฏิบัติที่ดีเนื่องจากหน่วยรวบรวมข้อมูลนั้นไม่รู้ว่าค่าของ foo คืออะไร มันรู้แค่ว่ามันเป็น const int และต้องโหลดค่าจากหน่วยความจำใหม่ทุกครั้งที่ใช้

ตอนนี้โดยการประกาศว่าเป็นแบบคงที่:

static const int foo = 42;

คอมไพเลอร์สามารถทำการปรับให้เหมาะสมได้ตามปกติ แต่ยังสามารถพูดว่า "เฮ้ไม่มีใครนอกหน่วยการรวบรวมนี้สามารถเห็น foo ได้และฉันรู้ว่ามันเป็น 42 เสมอดังนั้นจึงไม่จำเป็นต้องจัดสรรพื้นที่ใด ๆ สำหรับมัน"

ฉันควรสังเกตด้วยว่าใน C ++ วิธีที่ต้องการในการป้องกันไม่ให้ชื่อหนีจากหน่วยคอมไพล์ปัจจุบันคือการใช้เนมสเปซที่ไม่ระบุชื่อ:

namespace {
    const int foo = 42; // same as static definition above
}

1
คุณกล่าวถึงโดยไม่ใช้แบบคงที่ "มันจะจัดสรรพื้นที่ในพื้นที่ข้อมูลเริ่มต้นสำหรับมันด้วย" และโดยใช้แบบคงที่ "ไม่จำเป็นต้องจัดสรรพื้นที่ใด ๆ ให้" (จากที่คอมไพลเลอร์เลือกวาล) คุณสามารถอธิบายในรูปแบบของฮีปและสแต็กที่ตัวแปรถูกจัดเก็บได้แก้ไขฉันถ้าฉันกำลังตีความ ไม่ถูกต้อง .
Nihar

@ N Nihar - พื้นที่ข้อมูลคงที่เป็นหน่วยความจำขนาดคงที่ซึ่งมีข้อมูลทั้งหมดที่มีการเชื่อมโยงแบบคงที่ มัน "จัดสรร" โดยกระบวนการโหลดโปรแกรมลงในหน่วยความจำ ไม่ใช่ส่วนหนึ่งของสแต็กหรือฮีป
Ferruccio

จะเกิดอะไรขึ้นถ้าฉันมีฟังก์ชันส่งกลับตัวชี้ไปที่ foo นั่นทำลายการเพิ่มประสิทธิภาพหรือไม่
nw.

@nw: ใช่มันจะต้อง
Ferruccio

8

ไม่มี "int" มันควรจะเป็น:

const static int foo = 42;

ใน C และ C ++ จะประกาศค่าคงที่จำนวนเต็มพร้อมขอบเขตไฟล์โลคัลเป็นค่า 42

ทำไมต้อง 42? หากคุณไม่ได้รู้อยู่แล้วว่า (และมันยากที่จะเชื่อว่าคุณทำไม่ได้) มันเป็น refernce ไปที่คำตอบชีวิตจักรวาลและทุกอย่าง


ขอบคุณ ... ตอนนี้ทุกครั้ง ... ตลอดชีวิต ... เมื่อฉันเห็น 42 ฉันจะนึกถึงสิ่งนี้เสมอ haha
Inisheer

นี่เป็นข้อพิสูจน์ในเชิงบวกว่าจักรวาลถูกสร้างขึ้นโดยการมี 13 นิ้ว (คำถามและคำตอบตรงกับฐาน 13)
paxdiablo

มันคือหนู เท้าข้างละ 3 นิ้วบวกหางช่วยให้คุณมีฐาน 13
KeithB

คุณไม่จำเป็นต้องมี 'int' ในการประกาศแม้ว่ามันจะเป็นการดีที่จะเขียนมันออกมา C จะถือว่าประเภท 'int' เป็นค่าเริ่มต้นเสมอ ลองมัน!
ephemient

"พร้อมขอบเขตไฟล์ในเครื่องที่มีค่า 42" ?? หรือสำหรับหน่วยคอมไพล์ทั้งหมด?
aniliitb10

4

ใน C ++

static const int foo = 42;

เป็นวิธีที่ต้องการในการกำหนดและใช้ค่าคงที่ เช่นใช้สิ่งนี้มากกว่า

#define foo 42

เนื่องจากไม่ได้ล้มล้างระบบความปลอดภัยประเภท


4

สำหรับคำตอบที่ดีทั้งหมดฉันต้องการเพิ่มรายละเอียดเล็กน้อย:

หากคุณเขียนปลั๊กอิน (เช่น DLLs หรือ. so libraries ที่จะโหลดโดยระบบ CAD) แบบคงที่คือตัวช่วยชีวิตที่หลีกเลี่ยงการชนชื่อเช่นนี้:

  1. ระบบ CAD โหลดปลั๊กอิน A ซึ่งมี "const int foo = 42;" ในนั้น.
  2. ระบบจะโหลดปลั๊กอิน B ซึ่งมี "const int foo = 23;" ในนั้น.
  3. ด้วยเหตุนี้ปลั๊กอิน B จะใช้ค่า 42 สำหรับ foo เนื่องจากตัวโหลดปลั๊กอินจะรู้ว่ามี "foo" ที่มีการเชื่อมโยงภายนอกอยู่แล้ว

ยิ่งแย่ไปกว่านั้น: ขั้นตอนที่ 3 อาจทำงานแตกต่างกันไปขึ้นอยู่กับการเพิ่มประสิทธิภาพของคอมไพเลอร์กลไกการโหลดปลั๊กอิน ฯลฯ

ฉันมีปัญหานี้หนึ่งครั้งกับฟังก์ชันตัวช่วยสองตัว (ชื่อเดียวกันพฤติกรรมต่างกัน) ในสองปลั๊กอิน การประกาศแบบคงที่ช่วยแก้ปัญหาได้


มีบางอย่างที่ดูแปลกเกี่ยวกับการชนกันของชื่อระหว่างปลั๊กอินทั้งสองซึ่งทำให้ฉันต้องตรวจสอบแผนผังลิงก์สำหรับหนึ่งใน DLL จำนวนมากของฉันที่กำหนด m_hDfltHeap เป็นตัวจัดการที่มีการเชื่อมโยงภายนอก แน่นอนว่ามีไว้ให้ทั่วโลกได้เห็นและใช้งานโดยระบุไว้ในแผนที่เชื่อมโยงเป็น _m_hDfltHeap ฉันลืมเรื่องนี้ไปหมดแล้ว
David

4

ตามข้อกำหนด C99 / GNU99:

  • static

    • คือตัวระบุระดับพื้นที่เก็บข้อมูล

    • ออบเจ็กต์ของขอบเขตระดับไฟล์โดยค่าเริ่มต้นมีการเชื่อมโยงภายนอก

    • ออบเจ็กต์ของขอบเขตระดับไฟล์ที่มีตัวระบุแบบคงที่มีการเชื่อมโยงภายใน
  • const

    • คือ type-qualifier (เป็นส่วนหนึ่งของประเภท)

    • คำหลักที่ใช้กับอินสแตนซ์ด้านซ้ายทันที - เช่น

      • MyObj const * myVar; - ตัวชี้ที่ไม่มีเงื่อนไขไปยังประเภทวัตถุที่มีคุณสมบัติเหมาะสม

      • MyObj * const myVar; - กำหนดตัวชี้ที่มีคุณสมบัติตามประเภทวัตถุที่ไม่มีเงื่อนไข

    • การใช้งานซ้ายสุด - ใช้กับประเภทออบเจ็กต์ไม่ใช่ตัวแปร

      • const MyObj * myVar; - ตัวชี้ที่ไม่มีเงื่อนไขไปยังประเภทวัตถุที่มีคุณสมบัติเหมาะสม

ดังนี้:

static NSString * const myVar; - ตัวชี้คงที่ไปยังสตริงที่ไม่เปลี่ยนรูปพร้อมการเชื่อมโยงภายใน

การไม่มีstaticคำหลักจะทำให้ชื่อตัวแปรเป็นสากลและอาจนำไปสู่ความขัดแย้งของชื่อภายในแอปพลิเคชัน


4

C ++ 17 inlineตัวแปร

หากคุณใช้ "C ++ const คงที่" เป็นไปได้มากว่าสิ่งที่คุณต้องการใช้คือตัวแปรอินไลน์ C ++ 17ตัวแปร

คุณลักษณะ C ++ 17 ที่ยอดเยี่ยมนี้ช่วยให้เราสามารถ:

  • ใช้เพียงที่อยู่หน่วยความจำเดียวสำหรับค่าคงที่แต่ละค่า
  • จัดเก็บเป็นconstexpr:จะประกาศ constexpr extern ได้อย่างไร?
  • ทำในบรรทัดเดียวจากส่วนหัวเดียว

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

รวบรวมและเรียกใช้:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

GitHub อัปสตรีม

ดูเพิ่มเติม: ตัวแปรอินไลน์ทำงานอย่างไร

มาตรฐาน C ++ สำหรับตัวแปรอินไลน์

มาตรฐาน C ++ รับประกันว่าที่อยู่จะเหมือนกัน C ++ 17 N4659 ร่างมาตรฐาน 10.1.6 "ตัวระบุอินไลน์":

6 ฟังก์ชันอินไลน์หรือตัวแปรที่มีการเชื่อมโยงภายนอกต้องมีที่อยู่เดียวกันในทุกหน่วยการแปล

cppreference https://en.cppreference.com/w/cpp/language/inlineอธิบายว่าถ้าstaticไม่ได้ระบุแสดงว่ามีการเชื่อมโยงภายนอก

การใช้งานตัวแปรแบบอินไลน์ของ GCC

เราสามารถสังเกตวิธีการใช้งานได้ด้วย:

nm main.o notmain.o

ซึ่งประกอบด้วย:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i

และman nmพูดเกี่ยวกับu:

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

เราจึงเห็นว่ามีส่วนขยาย ELF สำหรับสิ่งนี้โดยเฉพาะ

ก่อน C ++ 17: extern const

ก่อน C ++ 17 และใน C เราสามารถบรรลุเอฟเฟกต์ที่คล้ายกันมากกับไฟล์ extern constซึ่งจะนำไปสู่การใช้ตำแหน่งหน่วยความจำเดียว

ข้อเสียinlineคือ:

  • เป็นไปไม่ได้ที่จะสร้างตัวแปรconstexprด้วยเทคนิคนี้inlineอนุญาตให้ทำได้เท่านั้น:จะประกาศ constexpr extern ได้อย่างไร
  • มีความสง่างามน้อยกว่าเนื่องจากคุณต้องประกาศและกำหนดตัวแปรแยกกันในไฟล์ header และ cpp

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.cpp

#include "notmain.hpp"

const int notmain_i = 42;

const int* notmain_func() {
    return &notmain_i;
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

extern const int notmain_i;

const int* notmain_func();

#endif

GitHub อัปสตรี

Pre-C ++ 17 ส่วนหัวทางเลือกเท่านั้น

สิ่งเหล่านี้ไม่ดีเท่าexternวิธีแก้ปัญหา แต่ใช้งานได้และใช้ตำแหน่งหน่วยความจำเดียวเท่านั้น:

constexprฟังก์ชั่นเพราะconstexprหมายถึงinlineและinline ช่วย (กองกำลัง) คำนิยามที่ปรากฏในหน่วยการแปลทุก :

constexpr int shared_inline_constexpr() { return 42; }

และฉันพนันได้เลยว่าคอมไพเลอร์ที่เหมาะสมจะอินไลน์

คุณยังสามารถใช้ a constหรือconstexprตัวแปรคงที่ใน:

#include <iostream>

struct MyClass {
    static constexpr int i = 42;
};

int main() {
    std::cout << MyClass::i << std::endl;
    // undefined reference to `MyClass::i'
    //std::cout << &MyClass::i << std::endl;
}

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

ใน C สถานการณ์จะเหมือนกับ C ++ ก่อน C ++ 17 ฉันได้อัปโหลดตัวอย่างที่: "คงที่" หมายความว่าอะไรใน C?

ข้อแตกต่างเพียงอย่างเดียวก็คือใน C ++ มีconstความหมายstaticสำหรับ globals แต่ไม่ได้อยู่ในความหมายของ C: C ++ ของ "static const" เทียบกับ "const"

มีวิธีใดที่จะอินไลน์ได้อย่างเต็มที่?

สิ่งที่ต้องทำ: มีวิธีใดบ้างในการแทรกตัวแปรโดยไม่ต้องใช้หน่วยความจำเลย?

เหมือนกับสิ่งที่พรีโปรเซสเซอร์ทำ

สิ่งนี้จะต้องมี:

  • ห้ามหรือตรวจจับว่ามีการใช้ที่อยู่ของตัวแปรหรือไม่
  • เพิ่มข้อมูลนั้นลงในไฟล์ออบเจ็กต์ ELF และปล่อยให้ LTO ปรับให้เหมาะสม

ที่เกี่ยวข้อง:

ทดสอบใน Ubuntu 18.10, GCC 8.2.0


2

ใช่มันซ่อนตัวแปรในโมดูลจากโมดูลอื่น ๆ ใน C ++ ฉันใช้เมื่อฉันไม่ต้องการ / จำเป็นต้องเปลี่ยนไฟล์. h ที่จะทำให้เกิดการสร้างไฟล์อื่นใหม่โดยไม่จำเป็น นอกจากนี้ฉันใส่ค่าคงที่ก่อน:

static const int foo = 42;

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


2

ค่าคงที่ทั่วโลกที่มองเห็นได้ / เข้าถึงได้เฉพาะในโมดูลคอมไพล์ (ไฟล์. cpp) BTW ที่ใช้แบบคงที่เพื่อการนี้เลิกใช้แล้ว ควรใช้เนมสเปซที่ไม่ระบุตัวตนและ enum:

namespace
{
  enum
  {
     foo = 42
  };
}

สิ่งนี้จะบังคับให้คอมไพเลอร์ไม่ถือว่า foo เป็นค่าคงที่และเป็นอุปสรรคต่อการเพิ่มประสิทธิภาพ
Nils Pipenbrinck

ค่า enums คงที่เสมอดังนั้นฉันจึงไม่เห็นว่าสิ่งนี้จะขัดขวางการเพิ่มประสิทธิภาพใด ๆ
Roskoto

อา - จริง .. ข้อผิดพลาดของฉัน คิดว่าคุณใช้ตัวแปร int แบบธรรมดา
Nils Pipenbrinck

Roskoto ฉันไม่ชัดเจนว่ามีประโยชน์อะไรenumในบริบทนี้ สนใจที่จะทำอย่างละเอียด? ดังกล่าวenumsมักจะถูกใช้เพียงเพื่อป้องกันไม่ให้คอมไพเลอร์จากการจัดสรรพื้นที่ใดสำหรับค่า (แม้ว่าคอมไพเลอร์ที่ทันสมัยไม่ต้องนี้enumสับมัน) และเพื่อป้องกันการสร้างตัวชี้ค่า
Konrad Rudolph

Konrad คุณเห็นปัญหาอะไรในการใช้ enum ในกรณีนี้? Enums ถูกใช้เมื่อคุณต้องการค่าคงที่ ints ซึ่งตรงกับกรณีนี้
Roskoto

1

การทำให้เป็นแบบส่วนตัวก็ยังคงหมายความว่าจะปรากฏในส่วนหัว ฉันมักจะใช้วิธีที่ "อ่อนแอที่สุด" ที่ได้ผล ดูบทความคลาสสิกนี้โดย Scott Meyers: http://www.ddj.com/cpp/184401197 (เป็นเรื่องเกี่ยวกับฟังก์ชัน แต่สามารถนำไปใช้ที่นี่ได้เช่นกัน)

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