const_cast ปลอดภัยหรือไม่?


92

const_castฉันไม่สามารถหาข้อมูลมากใน ข้อมูลเดียวที่ฉันพบ (ใน Stack Overflow) คือ:

const_cast<>()จะใช้ในการเพิ่ม / ลบ const (เนส) (หรือระเหย-Ness) ของตัวแปร

นี่ทำให้ฉันกังวล อาจconst_castทำให้เกิดพฤติกรรมที่ไม่คาดคิดได้หรือไม่? ถ้าเป็นเช่นนั้นคืออะไร?

อีกวิธีหนึ่งคือใช้เมื่อconst_castใด


4
คำตอบด้านบนมองข้ามบางสิ่งที่อาจเห็นได้ชัดอย่างน่ากลัว แต่ก็คุ้มค่าที่จะระบุ: มันจะไม่ปลอดภัยก็ต่อเมื่อคุณพยายามแก้ไขconstวัตถุเดิมผ่านการconstอ้างอิง / ตัวชี้ที่ไม่ได้กำหนดไว้ หาก แต่คุณเพียงแค่const_castต้องการหลีกเลี่ยง API spec'd ที่ไม่ดี (หรือในกรณีของฉันคือเกียจคร้าน) ที่ยอมรับเฉพาะการไม่constอ้างอิง แต่จะใช้ในconstวิธีการเท่านั้น ... ไม่มีปัญหาใด ๆ
underscore_d

1
@underscore_d: คำถาม (และคำตอบ) ในเวอร์ชันที่แม่นยำยิ่งขึ้นซึ่งครอบคลุมนั่นคือ: อนุญาตให้ทิ้ง const บนวัตถุที่กำหนด const ได้หรือไม่ตราบใดที่ยังไม่ได้แก้ไขจริง
Peter Cordes

คำตอบ:


89

const_castconstมีความปลอดภัยเท่านั้นถ้าคุณกำลังหล่อตัวแปรที่เป็นไม่ใช่เดิม ตัวอย่างเช่นหากคุณมีฟังก์ชันที่รับพารามิเตอร์ของ a const char *และคุณส่งผ่านค่าที่แก้ไขได้char *ก็ปลอดภัยที่จะให้const_castพารามิเตอร์นั้นกลับไปเป็น a char *และแก้ไข อย่างไรก็ตามหากตัวแปรดั้งเดิมเป็นจริงการconstใช้const_castจะส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด

void func(const char *param, size_t sz, bool modify)
{
    if(modify)
        strncpy(const_cast<char *>(param), sz, "new string");
    printf("param: %s\n", param);
}

...

char buffer[16];
const char *unmodifiable = "string constant";
func(buffer, sizeof(buffer), true);  // OK
func(unmodifiable, strlen(unmodifiable), false); // OK
func(unmodifiable, strlen(unmodifiable), true);  // UNDEFINED BEHAVIOR

9
มันไม่เป็นความจริง. มาตรฐาน C ++ §7.1.​5.1/4 says Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior พยายามใด ๆ ! ไม่มีคำเกี่ยวกับตัวแปรดั้งเดิม
Alexey Malistov

20
@ Alexey: ตัวแปรดั้งเดิมเกี่ยวกับสิ่งที่ชี้หรืออ้างถึง คุณสามารถใช้การอ้างอิง const ไปยังอ็อบเจ็กต์ที่ไม่ใช่ const ได้ดังนั้นการแคสต์ไปยังการอ้างอิงที่เขียนได้จึงเป็นพฤติกรรมที่กำหนดไว้อย่างดีเนื่องจากอ็อบเจ็กต์ที่อ้างถึงนั้นไม่ใช่ const
Puppy

43
@ Alexey Malistov: ไม่ "วัตถุ" หมายถึงพื้นที่จัดเก็บข้อมูลจริงที่อยู่ในหน่วยความจำ (§1.7) การอ้างอิง const ไปยังวัตถุที่ไม่ใช่ const ไม่ได้ทำให้วัตถุ const เฉพาะในกรณีของพารามิเตอร์อ้างอิง const ( ไม่ใช่พารามิเตอร์ตัวชี้ const) เท่านั้นที่คอมไพเลอร์จะได้รับอนุญาตให้ทำสำเนา (§5.2.2 / 5) นี่ไม่ใช่กรณีที่นี่
Adam Rosenfield

8
"อย่างไรก็ตามหากตัวแปรดั้งเดิมเป็น const จริงการใช้ const_cast จะทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด"คำสั่งนี้เป็นเท็จ
Lightness Races ใน Orbit

7
มันไม่ UB ที่จะใช้const_castในการลบจากสิ่งที่ถูกประกาศครั้งแรกconst constแต่มันเป็น UB ที่จะพยายามเขียนลงในวัตถุนั้นจริงๆ ตราบเท่าที่คุณอ่านคุณสบายดีและconst_castในตัวเองไม่ได้ทำให้เกิด UB เป็นความคิดที่น่ากลัว แต่มันไม่ใช่ UB โดยเนื้อแท้
Jesper Juhl

35

ฉันนึกถึงสองสถานการณ์ที่ const_cast ปลอดภัยและมีประโยชน์ (อาจมีกรณีอื่น ๆ ที่ถูกต้อง)

หนึ่งคือเมื่อคุณมีอินสแตนซ์การอ้างอิงหรือตัวชี้ const และคุณต้องการส่งผ่านตัวชี้หรือการอ้างอิงไปยัง API ที่ไม่ถูกต้อง แต่คุณ CERTAIN จะไม่แก้ไขวัตถุ คุณสามารถ const_cast ตัวชี้และส่งต่อไปยัง API ได้โดยเชื่อว่าจะไม่เปลี่ยนแปลงอะไรเลย ตัวอย่างเช่น:

void log(char* text);   // Won't change text -- just const-incorrect

void my_func(const std::string& message)
{
    log(const_cast<char*>(&message.c_str()));
}

อีกอย่างคือถ้าคุณใช้คอมไพเลอร์รุ่นเก่าที่ไม่ได้ใช้ 'mutable' และคุณต้องการสร้างคลาสที่มีตรรกะ แต่ไม่ใช่ const แบบบิต คุณสามารถ const_cast 'this' ภายในวิธีการ const และแก้ไขสมาชิกในชั้นเรียนของคุณ

class MyClass
{
    char cached_data[10000]; // should be mutable
    bool cache_dirty;        // should also be mutable

  public:

    char getData(int index) const
    {
        if (cache_dirty)
        {
          MyClass* thisptr = const_cast<MyClass*>(this);
          update_cache(thisptr->cached_data);
        }
        return cached_data[index];
    }
};

นี่ ... ดูเหมือนจะไม่ตอบคำถามนี้ เขาถามว่าconst_castสามารถทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดได้หรือไม่ไม่ใช่การใช้งานที่เป็นประโยชน์
Michael Mrozek

13
จากคำถาม: "หรืออีกวิธีหนึ่งคือเมื่อใดที่จะใช้ const_cast ได้"
Fred Larson

เช่นเดียวกับใน "เมื่อใดไม่ระบุ"; เขาไม่ได้มองหาตัวอย่างว่าเมื่อใดมีประโยชน์
Michael Mrozek

เราสามารถติดบนตัวอักษรของคำถามเท่านั้น จากสิ่งนี้การนำเสนอของการใช้ภาพประกอบconst_castจึงเป็นคำตอบที่ถูกต้อง ไม่มีheคำถามใด ๆ เนื่องจากคำถามเพียงอย่างเดียวคือหัวข้อ
eigenfield

24

ฉันคิดว่ามันยากที่จะเชื่อว่านั่นเป็นข้อมูลเดียวที่คุณสามารถหาได้เกี่ยวกับ const_cast การอ้างอิงจากGoogle hit ครั้งที่สอง :

หากคุณละทิ้งความมั่นคงของวัตถุที่ได้รับการประกาศอย่างชัดเจนว่าเป็น const และพยายามแก้ไขผลลัพธ์จะไม่ได้รับการกำหนด

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


คำตอบ Grrrreaat รวมสิ่งนี้กับคำตอบนี้และคุณจะได้ภาพรวม
bobobobo

อืม. เกี่ยวกับข้อความที่สองในคำตอบของคุณฉันขอถามคุณได้อย่างไรว่ามี "const" สำหรับวัตถุที่ไม่ได้ประกาศอย่างชัดเจนว่าเป็น const ตั้งแต่แรกได้อย่างไร?
hAcKnRoCk

มีหลายวิธีในการทำให้วัตถุที่ไม่ใช่ const เป็น const, @Iam ตัวอย่างเช่นส่งผ่านวัตถุเป็นพารามิเตอร์การอ้างอิง const หรือกำหนดให้กับ pointer-to-const หรือใช้const_cast. หรือเรียกวิธีการ const ก็ได้
Rob Kennedy

12

สิ่งที่อดัมพูด อีกตัวอย่างหนึ่งที่ const_cast มีประโยชน์:

struct sample {
    T& getT() { 
        return const_cast<T&>(static_cast<const sample*>(this)->getT()); 
    }

    const T& getT() const { 
       /* possibly much code here */
       return t; 
    }

    T t;
};

ก่อนอื่นเราเพิ่ม const ให้กับ type thispoint จากนั้นเราเรียกเวอร์ชัน const ของgetTจากนั้นเราจะลบ const ออกจากประเภท return ซึ่งถูกต้องเนื่องจากtต้องไม่ใช่ const (มิฉะนั้นเวอร์ชันที่ไม่ใช่ const getTจะไม่มี ถูกเรียก) สิ่งนี้จะมีประโยชน์มากหากคุณมีฟังก์ชันขนาดใหญ่และคุณต้องการหลีกเลี่ยงโค้ดซ้ำซ้อน


3
ฉันอยากจะใช้การร่ายแบบคงที่สำหรับการเพิ่ม constness: static_cast <const sample *> (this) เมื่อฉันอ่าน const_cast หมายความว่าโค้ดกำลังทำบางสิ่งที่อาจเป็นอันตรายดังนั้นฉันจึงพยายามหลีกเลี่ยงการใช้เมื่อเป็นไปได้
mfazekas

1
ใช่อย่างแรกอาจเป็น static_cast หรือแม้กระทั่ง implicit_cast (ของการเพิ่ม) ฉันจะแก้ไขโดยใช้การร่ายแบบคงที่ ขอบคุณ
Johannes Schaub - litb

3
ฉันกลับไปกลับมาว่าดีขึ้นconst_castหรือstatic_castไม่ const_castทำได้เฉพาะในสิ่งที่คุณต้องการ: เปลี่ยน cv-qualifiers static_castสามารถ "เงียบ" ดำเนินการอื่น ๆ ที่คุณไม่ได้ตั้งใจ อย่างไรก็ตามนักแสดงครั้งแรกที่มีความปลอดภัยทั้งหมดและมีแนวโน้มที่จะปลอดภัยกว่าstatic_cast const_castฉันคิดว่านี่เป็นสถานการณ์ที่const_castสื่อถึงเจตนาของคุณได้ดีขึ้น แต่การstatic_castสื่อสารสื่อสารถึงความปลอดภัยในการกระทำของคุณได้ดีกว่า
David Stone

10

คำตอบสั้น ๆ คือไม่มันไม่ปลอดภัย

คำตอบยาว ๆ คือถ้าคุณรู้จักพอที่จะใช้มันก็น่าจะปลอดภัย

เมื่อคุณแคสต์สิ่งที่คุณพูดเป็นหลักคือ "ฉันรู้ว่ามีอะไรที่คอมไพเลอร์ไม่รู้" ในกรณีของ const_cast สิ่งที่คุณกำลังพูดคือ "แม้ว่าวิธีนี้จะใช้การอ้างอิงหรือตัวชี้ที่ไม่ใช่ const แต่ฉันรู้ว่าจะไม่เปลี่ยนพารามิเตอร์ที่ฉันส่งผ่านไป"

ดังนั้นหากคุณรู้จริงในสิ่งที่คุณอ้างว่ารู้ในการใช้นักแสดงก็ใช้ได้ดี


4

คุณกำลังทำลายโอกาสใด ๆ ในเรื่องความปลอดภัยของเธรดหากคุณเริ่มแก้ไขสิ่งที่คอมไพเลอร์คิดว่าเป็น const


1
อะไร? ถ้าคุณมีไม่เปลี่ยนรูป (const) วัตถุที่คุณสามารถนิดแบ่งปันให้หมู่หัวข้อ ทันทีที่โค้ดของคุณหลุดลอยไปคุณจะสูญเสียความปลอดภัยทั้งหมดของเธรด! เหตุใดฉันจึงถูกปรับลดลงสำหรับสิ่งนี้ ถอนหายใจ
Matt Cruikshank

9
Const เป็นเครื่องมือที่มีประโยชน์ในการสร้างรหัสเธรดที่ปลอดภัย แต่ไม่รับประกัน (ยกเว้นในกรณีของค่าคงที่เวลาคอมไพล์) สองตัวอย่าง: วัตถุ const อาจมีสมาชิกที่เปลี่ยนแปลงได้และการมีตัวชี้ const ไปยังวัตถุนั้นไม่ได้บอกอะไรเลยว่าวัตถุนั้นสามารถเปลี่ยนแปลงได้หรือไม่
James Hopkin

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

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