需要对 C 风格、重新解释和 const 强制转换进行澄清

发布于 2024-08-23 22:04:04 字数 324 浏览 10 评论 0原文

我是否正确地假设 C 风格的强制转换(不鼓励这样做)只不过是 reinterpret_casts?使用后者在视觉上引人注目,并且在寻找令人讨厌的强制转换时易于搜索,因此建议使用它而不是 C 风格的强制转换?

如果使用 const_cast 抛弃 const 并写入原始 const 对象是未定义的,那么 const_cast 的目的是什么?

注意:我知道 Bjarne 正确地谴责了转换操作,认为它们是不安全的,甚至达到了“一个丑陋操作应该有一个丑陋的语法形式”的程度。因此,C++ 中的转换运算符显得冗长。所以我会尽量减少它们的使用。承诺。 :)

Am I right in assuming that C-style casts (which are discouraged) are nothing but reinterpret_casts? Using the latter is visually striking and easy to search when looking for nasty casts, and hence it's recommended over C-style casts?

If casting away const using const_cast and writing to a originally const object is undefined, what is the purpose of const_cast?

Note: I know that Bjarne rightly condemns casting operations that they are unsafe and even goes to the extent of stating "An ugly operation should have an ugly syntactic form." and hence the verbosity of casting operators in C++. So I'll try to minimize their usage. Promise. :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

与他有关 2024-08-30 22:04:04

不会。AC 强制转换可以执行与 const_caststatic_castreinterpret_cast 或其组合等效的操作。如果这还不够,它还可以做至少一个新演员组合根本无法做到的小技巧!

如果原始变量是在没有 const 的情况下定义的,则可以使用 const_cast 来定义结果,但您拥有的只是一个 const 指针或对该对象的引用。 OTOH,如果您认为您有充分的理由使用 const_cast,那么您可能真的应该查找 mutable

编辑:我想我应该立即说出来,但是 C 风格的强制转换可以转换为无法访问的基类。例如,考虑如下内容:

[编辑:我正在将代码更新为可以编译并(通常)演示问题的内容。 ]

#include <iostream>

class base1 {
public:
    virtual void print() { std::cout << "base 1\n"; }
};

class base2 {
public:
   virtual void print() { std::cout << "base 2\n"; }
};

class derived : base1, base2 {}; // note: private inheritance

int main() {    
    derived *d = new derived;
    base1 *b1 = (base1 *)d;    // allowed
    b1->print();    // prints "base 1"
    base2 *b2 = (base2 *)d;    // also allowed
    b2->print();    // prints "base 2"

//    base1 *bb1 = static_cast<base *>(d);  // not allowed: base is inaccessible

    // Using `reinterpret_cast` allows the code to compile.
    // Unfortunately the result is different, and normally won't work. 
    base1 *bb2 = reinterpret_cast<base1 *>(d);
    bb2->print();   // may cause nasal demons.

    base2 *bb3 = reinterpret_cast<base2 *>(d); 
    bb3->print();   // likewise
    return 0;
}

使用reinterpret_cast的代码将编译——但是尝试使用结果(至少两者之一)将导致一个主要问题。 reinterpret_cast 获取派生对象的地址,并尝试将其视为指定类型的基对象 - 并且因为(最多)一个基对象实际上可以存在于该地址,尝试将其视为其他地址可能/将导致重大问题。编辑:在这种情况下,除了打印的内容之外,这些类本质上是相同的,因此尽管任何事情都可能发生,但对于大多数编译器来说,最后两个都会打印出“base 1”。 reinterpret_cast 接受该地址处发生的任何内容,并尝试将其用作指定的类型。在这种情况下,我(试图)让它做一些无害但可见的事情。在实际代码中,结果可能不会那么漂亮。

如果代码使用公共继承而不是私有继承,C 风格的强制转换将像 static_cast 一样工作——即它知道派生类中每个基类对象“存在”的位置,并调整结果,因此每个结果指针将工作,因为它已被调整为指向正确的位置。

No. A C cast can do the equivalent of a const_cast, a static_cast, a reinterpret_cast, or a combination thereof. In case that wasn't quite enough, it can also do at least one minor trick that no combination of the newer casts can do at all!

You can use const_cast with defined results if the original variable is defined without const, but all you have is a const pointer or reference to that object. OTOH, if you think you have a good reason to use a const_cast, chances are that you should really look up mutable instead.

Edit: I suppose I should have said it right off, but a C-style cast can convert to an an inaccessible base class. For example, consider something like:

[Edit: I'm updating the code to something that'll compile and (usually) demonstrate problem. ]

#include <iostream>

class base1 {
public:
    virtual void print() { std::cout << "base 1\n"; }
};

class base2 {
public:
   virtual void print() { std::cout << "base 2\n"; }
};

class derived : base1, base2 {}; // note: private inheritance

int main() {    
    derived *d = new derived;
    base1 *b1 = (base1 *)d;    // allowed
    b1->print();    // prints "base 1"
    base2 *b2 = (base2 *)d;    // also allowed
    b2->print();    // prints "base 2"

//    base1 *bb1 = static_cast<base *>(d);  // not allowed: base is inaccessible

    // Using `reinterpret_cast` allows the code to compile.
    // Unfortunately the result is different, and normally won't work. 
    base1 *bb2 = reinterpret_cast<base1 *>(d);
    bb2->print();   // may cause nasal demons.

    base2 *bb3 = reinterpret_cast<base2 *>(d); 
    bb3->print();   // likewise
    return 0;
}

The code using the reinterpret_casts will compile -- but attempting to use the result (of at lest one of the two) will cause a major problem. The reinterpret_cast takes the base address of the derived object and attempts to treat it as if it was the specified type of base object -- and since (at most) one base object can actually exist at that address, trying to treat it as the other can/will cause major problems. Edit: In this case, the classes are essentially identical except for what they print, so although anything could happen, with most compilers, both of the last two will print out "base 1". The reinterpret_cast takes whatever happens to be at that address and tries to use it as the specified type. In this case, I've (tried to) make that do something harmless but visible. In real code, the result probably won't be so pretty.

The C-style cast will work like a static_cast would if the code had used public inheritance instead of private -- i.e. it's aware of where in the derived class each base class object "lives", and adjusts the result, so each resulting pointer will work because it's been adjusted to point at the right place.

我很坚强 2024-08-30 22:04:04

不,C 风格的转换可以根据情况充当 reinterpret_castconst-caststatic_cast。这就是他们灰心丧气的原因——您在代码中看到 C 风格的转换,需要查找详细信息以了解它会做什么。例如:

const char* source;
int* target = (int*)source;// - acts as const_cast and reinterpret_cast at once
//int* target = retinterpret_cast<int*>source;// - won't compile - can't remove const

No, C-style casts can act as reinterpret_casts, const-casts or static_casts depending on the situation. This is why they are discouraged - you see a C-style cast in code and need to look for details to see what it will do. For example:

const char* source;
int* target = (int*)source;// - acts as const_cast and reinterpret_cast at once
//int* target = retinterpret_cast<int*>source;// - won't compile - can't remove const
九歌凝 2024-08-30 22:04:04

请记住,const 强制转换可能会作用于原始标识符以外的其他内容:

void doit(const std::string &cs)
{
    std::string &ms = const_cast<std::string &>(cs);
}

int main()
{
    std::string s;
    doit(s);
}

因此,虽然 doit 强制转换为 const,但在此示例中,底层字符串不是 const,因此没有未定义的行为。

更新

好的,这是一个更好的例子,说明使用 const_cast 并非完全没有价值。我们从带有一个带有 const 参数的虚函数的基类开始:

class base
{
public:
    virtual void doit(const std::string &str);
};

现在您想要重写该虚函数。

class child : public base
{
public:
    virtual void doit(const std::string &str)
    {
        std::string &mstr = const_cast<std::string &>(str);
    }
};

由于代码的逻辑/结构,您知道 child::doit 只会使用非常量字符串调用(并且 class base 不在您的控制之下,因此您无法修改它,也无法更改 child::doit 的签名,因为这样它将不再覆盖 base::doit)。在这种情况下,丢弃 const 是安全的。

是的,这是有风险的。也许当你这样写的时候,执行确实永远不会到达带有非常量字符串的 child::doit 并且代码是有效的。但是,在维护您的程序时,或者在您重建并获取最新版本的类库时,这种情况可能会发生变化。

Remember, that a const cast may be acting on something other then the original identifier:

void doit(const std::string &cs)
{
    std::string &ms = const_cast<std::string &>(cs);
}

int main()
{
    std::string s;
    doit(s);
}

So while doit is casting away const, in this example the underlying string is not const so no undefined behavior.

Update

Okay, here's a better example of when using const_cast is not completely worthless. We start with a base class with a virtual function that takes a const parameter:

class base
{
public:
    virtual void doit(const std::string &str);
};

and now you want to override that virtual function.

class child : public base
{
public:
    virtual void doit(const std::string &str)
    {
        std::string &mstr = const_cast<std::string &>(str);
    }
};

Because of the logic/structure of your code, you know that child::doit will only be called with non-const strings (and class base is not under your control so you can't modify it nor can you change the signature of child::doit because then it will not longer override base::doit). In this case, it's safe to cast away const.

Yes, this is risky. Perhaps when you write that, it's true that the execution will never reach child::doit with a non-const string and the code is valid. But that could change either while maintaining your program or perhaps when you rebuild and pick up the latest version of class base.

梦萦几度 2024-08-30 22:04:04

const_cast 用于从类型中删除 const。它还可以删除易失性。如果对象确实是 const,则结果无法写入,但仍然是明确定义的行为。但是,如果将其提升为 const(通过传递到 const T 函数),则将其 const_cast 返回为非>const 可以。(我在此处找到了更多信息)

reinterpret_cast 无法从类型中删除 constvolatile

。 com/questions/332030/when-should-staticcast-dynamiccast-and-reinterpretcast-be-used">另请参阅

const_cast is used to remove const from a type. It also can remove volatile. If the object really is const then the result cannot be written to and still be well-defined behavior. If, however, it is promoted to const (by being passed into a const T function, then const_casting it back to non-const is ok. ( i found some more info here)

reinterpret_cast cannot remove const or volatile from a type.

see also

我ぃ本無心為│何有愛 2024-08-30 22:04:04

C 风格的强制转换实际上是编程的大锤——你基本上告诉编译器,无论如何,那边的方钉都会穿过这个圆孔。从这个意义上说,reinterpret_cast 非常相似。

我认为使用 C++ 风格的强制转换运算符的主要优点是,它们允许您更好地表达您的意图,并允许编译器仍然对您要求其执行的操作进行一些检查,而不是一刀切地检查-全C风格演员阵容。

关于 const_cast - 您经常会遇到通过 const 引用传递对象的情况,仅仅是因为 API 要求您这样做。假设您有一个处理 C 风格字符串的函数 X:

void X(const char *str) { ... }

在该函数内,您将参数传递给需要 char * 的 C 函数,即使它没有更改字符串。适应这种情况的唯一方法是 const_cast str.

我会非常小心地使用任何类型的转换,通常这表明您的设计有些不太正确,但有时您必须说服编译器它正在查看的挂钩并不像它假设的那样方形。只有这样你才应该使用强制转换运算符。

C-style casts are really the sledge hammer of programming - you basically tell the compiler that the square peg over there will fit through this round hole no matter what. In that sense, reinterpret_cast is very similar.

The main advantage I see in using the C++-style cast operators are that they allow you to express your intent better and allow the compiler to still to some checking on the operation you're asking it to perform rather than the one-size-fits-all style C cast.

Regarding const_cast- you often get into the situation where you are passing an object around via const reference simply because the API requires you to do this. Say, you've got function X that tasks a C-style string:

void X(const char *str) { ... }

Inside that function you're passing the parameter to a C function that expects a char *, even though it's not changing the string. The only way to accommodate this would be to const_cast str.

I'd be very careful using any sort of cast, often this shows that there is something not quite right with your design but sometimes you have to convince the compiler that the peg it's looking at isn't as square as it assumes. Only then should you use the cast operators.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文