const_cast是否安全?

96

我找不到太多关于const_cast的信息。 我唯一能在Stack Overflow上找到的信息是:

const_cast<>()用于添加/删除变量的const(ness)(或volatile-ness)。

这让我很紧张。 使用const_cast会导致意外行为吗? 如果是这样,会发生什么情况?

或者,什么时候可以使用const_cast


4
顶部答案忽略了一个可能非常明显但值得说明的事情:只有当你试图通过一个去除了const属性的引用/指针来修改原始的“const”对象时,它才会变得不安全。如果相反,你只是使用const_cast来解决一个API规范(在我这种情况下是懒散的)只接受非“const”引用但仅将在“const”方法中使用的问题…那就没有任何问题。 - underscore_d
2
@underscore_d:一个更精确的问题(和答案)是:只要不实际修改,是否允许在const定义的对象上强制转换const? - Peter Cordes
6个回答

93

const_cast只有在你转换的变量原本不是const的情况下才是安全的。例如,如果你有一个函数,其参数为const char *,而你传入了一个可修改的char *,那么将该参数重新转换为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

11
不正确。这是C++标准中的内容。§7.1.​5.1/4说道:“除了任何声明为mutable (7.1.1)的类成员可以被修改之外,在其生命周期内试图修改const对象都会导致未定义行为。” 任何尝试!没有提到原始变量的任何字眼。 - Alexey Malistov
23
@Alexey:原始的变量是关于所指或所引用的内容。您可以对非const对象进行const引用,因此将其转换为可写引用是良好定义行为,因为所引用的对象实际上不是const。 - Puppy
45
@Alexey Malistov:不行。 "object" 指的是在内存中实际占用的存储区域(§1.7)。对非 const 对象取 const 引用并不会使对象变为 const。只有在 const 引用 参数(而不是 const 指针参数)的情况下,编译器才可以默默地进行复制(§5.2.2/5);但这里不是这种情况。 - Adam Rosenfield
9
这个说法是错误的。然而,如果原始变量确实是const类型,那么使用const_cast将导致未定义的行为。 - Lightness Races in Orbit
12
使用const_cast从最初声明为const的对象中去除const并不属于未定义行为。但是,实际尝试写入该对象将导致未定义行为。只要你只是读取该对象,那么你就没有问题,const_cast本身并不会引起未定义行为。这是一个可怕的想法,但它本身并不会导致未定义行为。 - Jesper Juhl
显示剩余8条评论

36

我可以想到两种情况下const_cast是安全和有用的(可能还有其他有效的情况)。

一种情况是当您有一个常量实例、引用或指针,并且您想要传递一个指向不符合const规定的API的指针或引用,但您确定该API不会修改对象。您可以使用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的类。您可以在const方法中使用const_cast 'this'并修改类成员。

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
15
什么情况下可以使用 const_cast? - Fred Larson
就像“何时它不是未定义的”一样;他不是在寻找它有用的例子。 - Michael Mrozek
2
我们只能固守问题的内容。基于此,提供一个 const_cast 的示例用法是一个有效的答案。问题中没有“他”,因为问题本身就是主语。 - daparic

25

我很难相信这是你能在const_cast上找到的唯一信息。引用自第二个Google搜索结果:

如果你强制转换一个明确声明为常量的对象,并试图修改它,结果是未定义的。

然而,如果你强制转换一个未经显式声明为常量的对象的常量性,你可以安全地修改它。


非常好的答案,将其与此答案结合起来,你就能得到整个画面。 - bobobobo
关于你回答中的第二个语句,我想问一下,如果一个对象在一开始没有被明确声明为const,那么它怎么会有“const”属性呢? - hAcKnRoCk
有很多方法可以使一个非const对象成为const,@Iam。例如,将对象作为const引用参数传递。或将其分配给指向const的指针。或使用const_cast。或调用一个const方法。 - Rob Kennedy

13

Adam说的话。另一个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;
};

我们首先将类型 this 指向的内容添加 const,然后调用 getT 的 const 版本,最后我们从返回值类型中删除 const,由于 t 必须是非 const 的(否则就不能调用非 const 版本的 getT),这是有效的。如果您有一个庞大的函数体并且想要避免冗余代码,则此方法非常有用。

3
我更倾向于使用 static_cast 来添加 const 属性:static_cast<const sample*>(this)。当我看到 const_cast 时,意味着代码可能存在潜在的危险,因此我尽可能避免使用它。 - mfazekas
1
没错,第一个可以使用static_cast,甚至可以使用boost的implicit_cast。我会使用static_cast进行修复。谢谢。 - Johannes Schaub - litb
3
我对const_caststatic_cast哪个更好一直有不同的看法。const_cast只能做你想要的事情:改变cv限定符。而static_cast则可能会“默默地”执行其他你不打算进行的操作。然而,第一个转换完全是安全的,static_cast通常比const_cast更安全。我认为这是一个场景,const_cast更能表达你的意图,但static_cast更能传达你的行动安全性。 - David Stone

10

简短回答是不安全的。

长话短说,如果您足够了解如何使用,则应该是安全的。

当您进行类型转换时,实际上是在说:“我知道编译器不知道的东西。”在const_cast的情况下,您要表达的是:“即使这个方法接受一个非const的引用或指针,我知道它不会改变我传递的参数。”

因此,如果您确实知道自己在使用转换时所声称的内容,那么使用它是可以的。


3

如果您开始修改编译器认为是常量的内容,那么您将破坏线程安全性的任何可能性。


1
什么?如果您有不可变(const)对象,您可以在线程之间轻松地共享它们。一旦您的代码将const状态转换为非const状态,您就会失去所有线程安全性!为什么我因此被降低评分?叹气 - Matt Cruikshank
9
Const在编写线程安全代码时确实是一个有用的工具,但它并不提供任何保证(除了编译时常量的情况)。举两个例子:一个const对象可能具有可变成员,而拥有一个指向对象的const指针并不意味着该对象本身不会发生改变。 - James Hopkin
我认为这是一个好答案,因为我没有考虑到编译器优化器在您使用“const”一词时的信任和安全感。const是信任。const_cast则破坏了这种信任:( - bobobobo
1
关于可变性和线程安全:http://channel9.msdn.com/posts/C-and-Beyond-2012-Herb-Sutter-You-dont-know-blank-and-blank - MFH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接