Const_cast có an toàn không?


92

Tôi không thể tìm thấy nhiều thông tin về const_cast. Thông tin duy nhất tôi có thể tìm thấy (trên Stack Overflow) là:

Các const_cast<>()được sử dụng để Add / Remove const (Ness) (hoặc không ổn định-Ness) của một biến.

Điều này khiến tôi lo lắng. Việc sử dụng có thể const_castgây ra hành vi không mong muốn không? Nếu vậy thì sao?

Ngoài ra, khi nào thì sử dụng được const_cast?


4
Câu trả lời hàng đầu bỏ qua một cái gì đó có thể rất rõ ràng nhưng đáng nói: Nó chỉ trở nên không an toàn nếu bạn cố gắng sửa đổi một constđối tượng ban đầu thông qua một consttham chiếu / con trỏ de -ed. Thay vào đó, nếu bạn chỉ đơn thuần const_castlàm việc xung quanh một API chỉ định kém (hoặc, trong trường hợp của tôi là lười biếng) chỉ chấp nhận một consttham chiếu không phải là tham chiếu nhưng sẽ chỉ được sử dụng trong constcác phương thức ... không vấn đề gì.
underscore_d

1
@underscore_d: Một phiên bản chính xác hơn của câu hỏi (và câu trả lời) bao gồm: Có được phép loại bỏ const trên một đối tượng do const xác định miễn là nó không thực sự được sửa đổi?
Peter Cordes

Câu trả lời:


88

const_castchỉ an toàn nếu bạn đang truyền một biến ban đầu không phải const. Ví dụ: nếu bạn có một hàm nhận tham số là a const char *và bạn chuyển vào một tham số có thể sửa đổi char *, thì sẽ an toàn khi const_castquay lại tham số đó về a char *và sửa đổi nó. Tuy nhiên, nếu thực tế là biến ban đầu const, thì việc sử dụng const_castsẽ dẫn đến hành vi không xác định.

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
Nó không đúng. Chuẩn 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 Cố gắng nào ! Không có từ nào về biến gốc.
Alexey Malistov

20
@Alexey: Biến ban đầu là về những gì được trỏ đến hoặc được tham chiếu. Bạn có thể lấy một tham chiếu const đến một đối tượng không phải const và do đó, truyền nó tới một tham chiếu có thể ghi là hành vi được xác định rõ ràng vì đối tượng được tham chiếu thực sự không phải là const.
Puppy

43
@Alexey Malistov: Không. "Đối tượng" đề cập đến vùng lưu trữ thực tế bị chiếm trong bộ nhớ (§1.7). Lấy tham chiếu const đến một đối tượng không phải const không làm cho đối tượng const. Chỉ trong trường hợp tham số tham chiếu const ( không phải tham số con trỏ const) thì trình biên dịch mới được phép tạo một bản sao âm thầm (§5.2.2 / 5); Đây không phải là trường hợp ở đây.
Adam Rosenfield

8
"Tuy nhiên, nếu biến ban đầu trên thực tế là const, thì việc sử dụng const_cast sẽ dẫn đến hành vi không xác định" Câu này sai.
Các cuộc đua ánh sáng ở Orbit

7
không phải UB được sử dụng const_castđể xóa constkhỏi một cái gì đó đã được khai báo ban đầu const. Nhưng nó UB để thực sự cố gắng để ghi vào đối tượng đó. Miễn là bạn chỉ đọc bạn là ổn và const_castbản thân nó không gây ra UB. Đó là một ý tưởng kinh khủng, nhưng nó không phải là UB.
Jesper Juhl

35

Tôi có thể nghĩ đến hai tình huống mà const_cast an toàn và hữu ích (có thể có những trường hợp hợp lệ khác).

Một là khi bạn có một cá thể, tham chiếu hoặc con trỏ const và bạn muốn chuyển một con trỏ hoặc tham chiếu đến một API không đúng hằng số, nhưng bạn CHẮC CHẮN sẽ không sửa đổi đối tượng. Bạn có thể const_cast con trỏ và chuyển nó đến API, tin tưởng rằng nó sẽ không thực sự thay đổi bất cứ điều gì. Ví dụ:

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

Trường hợp khác là nếu bạn đang sử dụng trình biên dịch cũ hơn không triển khai 'có thể thay đổi' và bạn muốn tạo một lớp theo logic const nhưng không phải const bit. Bạn có thể const_cast 'this' trong một phương thức const và sửa đổi các thành viên trong lớp của bạn.

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];
    }
};

Điều này ... dường như không trả lời câu hỏi này. Anh ấy hỏi liệu const_castcó thể gây ra hành vi không xác định chứ không phải ứng dụng hữu ích của nó là gì
Michael Mrozek

13
Từ câu hỏi: "Ngoài ra, khi nào thì sử dụng const_cast?"
Fred Larson

Như trong "when is it not undefined"; anh ấy không tìm kiếm ví dụ về thời điểm hữu ích
Michael Mrozek

Chúng tôi chỉ có thể bám vào các chữ cái của câu hỏi. Dựa trên điều này, việc trình bày cách sử dụng minh họa của const_castlà một câu trả lời hợp lệ. Không có hecâu hỏi nào vì một mình câu hỏi là chủ đề.
eigenfield

24

Tôi khó tin rằng đó là thông tin duy nhất bạn có thể tìm thấy về const_cast. Trích dẫn từ lần truy cập thứ hai của Google :

Nếu bạn loại bỏ hằng số của một đối tượng đã được khai báo rõ ràng là const và cố gắng sửa đổi nó, kết quả là không xác định.

Tuy nhiên, nếu bạn loại bỏ hằng số của một đối tượng chưa được khai báo rõ ràng là const, bạn có thể sửa đổi nó một cách an toàn.


Câu trả lời Grrrreaat, kết hợp câu trả lời này với câu trả lời này và bạn sẽ có được bức tranh toàn cảnh.
bobobobo

hmm. liên quan đến câu lệnh thứ hai trong câu trả lời của bạn, tôi có thể hỏi bạn làm thế nào có "const" cho một đối tượng không được khai báo rõ ràng là const ngay từ đầu ?.
hAcKnRoCk

Có rất nhiều cách để biến một đối tượng không phải là const, @Iam. Ví dụ: truyền đối tượng dưới dạng tham số tham chiếu const. Hoặc gán nó cho một con trỏ đến const. Hoặc sử dụng const_cast. Hoặc gọi một phương thức const trên đó.
Rob Kennedy

12

Adam nói gì. Một ví dụ khác mà const_cast có thể hữu ích:

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

Đầu tiên chúng ta thêm const vào kiểu thistrỏ tới, sau đó chúng ta gọi phiên bản const của getTvà sau đó chúng ta xóa const khỏi kiểu trả về, kiểu này hợp lệ vì tphải không phải là const (nếu không, phiên bản không phải const của getTkhông thể có được gọi). Điều này có thể rất hữu ích nếu bạn có một nội dung hàm lớn và bạn muốn tránh mã thừa.


3
Tôi muốn sử dụng ép kiểu tĩnh để thêm hằng số: static_cast <const sample *> (this). Khi tôi đang đọc const_cast, điều đó có nghĩa là mã đang làm điều gì đó nguy hiểm tiềm ẩn, vì vậy tôi cố gắng tránh sử dụng nó khi có thể.
mfazekas

1
đúng rồi, đầu tiên có thể là static_cast, hoặc thậm chí là implicit_cast (of boost). tôi sẽ sửa nó bằng cách sử dụng truyền tĩnh. cảm ơn
Johannes Schaub - litb

3
Tôi quay trở lại và ra về việc liệu const_casthoặc static_castlà tốt hơn. const_castchỉ có thể làm những gì bạn muốn: thay đổi các tiêu chuẩn cv. static_castcó thể 'âm thầm' thực hiện các thao tác khác mà bạn không có ý định. Tuy nhiên, lần bó bột đầu tiên hoàn toàn an toàn và static_castcó xu hướng an toàn hơn const_cast. Tôi nghĩ rằng đây là một tình huống mà người đó const_casttruyền đạt ý định của bạn tốt hơn, nhưng lại static_casttruyền đạt sự an toàn cho hành động của bạn tốt hơn.
David Stone

10

Câu trả lời ngắn gọn là không, nó không an toàn.

Câu trả lời dài là nếu bạn biết đủ để sử dụng nó, thì nó sẽ an toàn.

Khi bạn đang truyền, những gì bạn đang nói về cơ bản là, "Tôi biết điều gì đó mà trình biên dịch không biết." Trong trường hợp của const_cast, những gì bạn đang nói là, "Mặc dù phương thức này nhận tham chiếu hoặc con trỏ không phải const, tôi biết rằng nó sẽ không thay đổi tham số tôi truyền vào."

Vì vậy, nếu bạn thực sự biết những gì bạn đang tuyên bố biết trong việc sử dụng diễn viên, thì bạn có thể sử dụng nó.


5

Bạn đang phá hủy bất kỳ cơ hội nào về an toàn luồng, nếu bạn bắt đầu sửa đổi những thứ mà trình biên dịch nghĩ là const.


1
Gì? Nếu bạn có bất biến (const) đối tượng, bạn có thể trivially chia sẻ chúng giữa các chủ đề. Ngay khi một đoạn mã của bạn loại bỏ const-ness, bạn sẽ mất tất cả sự an toàn của chuỗi của mình! Tại sao tôi lại hạ cấp cho điều này? thở dài
Matt Cruikshank

8
Const chắc chắn là một công cụ hữu ích trong việc đảm bảo an toàn cho chuỗi mã, nhưng nó không có gì đảm bảo (ngoại trừ trường hợp là hằng số thời gian biên dịch). Hai ví dụ: một đối tượng const có thể có các thành phần có thể thay đổi và việc có một con trỏ const tới một đối tượng không nói gì về việc liệu bản thân đối tượng có thể thay đổi hay không.
James Hopkin

Tôi nghĩ đây là một câu trả lời hay vì tôi không nghĩ về cảm giác tin tưởng và bảo mật của trình tối ưu hóa trình biên dịch khi bạn sử dụng từ này const. constlà sự tin tưởng. const_castđang phá vỡ sự tin tưởng đó :(
bobobobo

1
Liên quan đến an toàn có thể thay đổi và luồng: channel9.msdn.com/posts/…
MFH

-3
#include <iostream>
using namespace std;

void f(int* p) {
  cout << *p << endl;
}

int main(void) {
  const int a = 10;
  const int* b = &a;

  // Function f() expects int*, not const int*
  //   f(b);
  int* c = const_cast<int*>(b);
  f(c);

  // Lvalue is const
  //  *b = 20;

  // Undefined behavior
  //  *c = 30;

  int a1 = 40;
  const int* b1 = &a1;
  int* c1 = const_cast<int*>(b1);

  // Integer a1, the object referred to by c1, has
  // not been declared const
  *c1 = 50;

  return 0;
}

nguồn: http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fkeyword_const_cast.htm


Phản đối do liên kết spam
eigenfield
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.