C ++类型转换常见问题 [英] C++ type conversion FAQ

查看:52
本文介绍了C ++类型转换常见问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在哪里可以找到关于C ++类型转换的涵盖所有类型(促销,隐式/显式等)的非常容易理解的文章?

Where I can find an excellently understandable article on C++ type conversion covering all of its types (promotion, implicit/explicit, etc.)?

我学习C ++已有一段时间了,例如,虚拟函数机制对我来说比本主题更清晰.我认为这是由于教科书的作者过于复杂(请参阅Stroustroup的书等).

I've been learning C++ for some time and, for example, virtual functions mechanism seems clearer to me than this topic. My opinion is that it is due to the textbook's authors who are complicating too much (see Stroustroup's book and so on).

推荐答案

(向Crazy Eddie提出第一个答案,但我认为可以使它更清晰)

(Props to Crazy Eddie for a first answer, but I feel it can be made clearer)

发生类型转换的原因主要有两个.一个是因为您编写了一个明确的表达式,例如 static_cast< int>(3.5).另一个原因是您在编译器需要其他类型的地方使用了表达式,因此它将为您插入转换.例如. 2.5 + 1 将导致从1(整数)到1.0(双精度)的隐式转换.

Type conversion can happen for two main reasons. One is because you wrote an explicit expression, such as static_cast<int>(3.5). Another reason is that you used an expression at a place where the compiler needed another type, so it will insert the conversion for you. E.g. 2.5 + 1 will result in an implicit cast from 1 (an integer) to 1.0 (a double).

仅有少量的显式形式.首先,C ++具有4个命名版本: static_cast dynamic_cast reinterpret_cast const_cast .C ++还支持C样式的强制转换(Type)Expression .最后,有一个构造函数样式"的强制转换 Type(Expression).

There are only a limited number of explicit forms. First off, C++ has 4 named versions: static_cast, dynamic_cast, reinterpret_cast and const_cast. C++ also supports the C-style cast (Type) Expression. Finally, there is a "constructor-style" cast Type(Expression).

4种命名形式均以任何良好的介绍性文字记录在案.C样式强制转换扩展为 static_cast const_cast reinterpret_cast ,而"constructor-style"强制转换是的简写> static_cast< Type> .但是,由于解析问题,构造函数样式"强制转换要求类型名称使用单一标识符. unsigned int(-5) const float(5)不合法.

The 4 named forms are documented in any good introductory text. The C-style cast expands to a static_cast, const_cast or reinterpret_cast, and the "constructor-style" cast is a shorthand for a static_cast<Type>. However, due to parsing problems, the "constructor-style" cast requires a singe identifier for the name of the type; unsigned int(-5) or const float(5) are not legal.

枚举可能发生隐式转换的所有上下文要困难得多.由于C ++是一种类型安全的OO语言,因此在许多情况下,在需要类型B的上下文中有一个对象A.示例是内置运算符,调用函数或按值捕获异常.

It's much harder to enumerate all the contexts in which an implicit conversion can happen. Since C++ is a typesafe OO language, there are many situations in which you have an object A in a context where you'd need a type B. Examples are the built-in operators, calling a function, or catching an exception by value.

在所有情况下,无论是隐式的还是显式的,编译器都将尝试查找转换序列.转换序列是使您从类型A转换为类型B的一系列步骤.编译器选择的确切转换序列取决于强制类型转换.使用 dynamic_cast 进行从Base到Derived的转换,因此步骤是检查Derived是否从Base继承,中间类是通过Base继承的. const_cast 可以同时删除 const volatile .对于 static_cast ,可能的步骤是最复杂的.它将在内置的算术类型之间进行转换;它将基本指针转换为派生指针,反之亦然,将考虑类构造函数(目标类型)和类强制转换运算符(源类型),并将添加 const 易失性.显然,这些步骤中有很多是正交的:算术类型永远不会是指针或类类型.此外,编译器最多将每个步骤使用一次.

In all cases, implicit and explicit, the compiler will try to find a conversion sequence. A conversion sequence is a series of steps that gets you from type A to type B. The exact conversion sequence chosen by the compiler depends on the type of cast. A dynamic_cast is used to do a checked Base-to-Derived conversion, so the steps are to check whether Derived inherits from Base, via which intermediate class(es). const_cast can remove both const and volatile. In the case of a static_cast, the possible steps are the most complex. It will do conversion between the built-in arithmetic types; it will convert Base pointers to Derived pointers and vice versa, it will consider class constructors (of the destination type) and class cast operators (of the source type), and it will add const and volatile. Obviously, quite a few of these step are orthogonal: an arithmetic type is never a pointer or class type. Also, the compiler will use each step at most once.

正如我们前面提到的,某些类型转换是显式的,而其他类型转换是隐式的.这对 static_cast 很重要,因为它在转换序列中使用了用户定义的函数.编译器考虑的某些转换步骤可以标记为 explicit (在C ++ 03中,只有构造函数可以).编译器将为隐式转换序列跳过(无错误)任何 explicit 转换函数.当然,如果没有其他选择,编译器仍然会给出错误.

As we noted earlier, some type conversions are explicit and others are implicit. This matters to static_cast because it uses user-defined functions in the conversion sequence. Some of the conversion steps consiered by the compiler can be marked as explicit (In C++03, only constructors can). The compiler will skip (no error) any explicit conversion function for implicit conversion sequences. Of course, if there are no alternatives left, the compiler will still give an error.

整数类型(例如 char short )可以转换为更大的"类型,例如 int long ,较小的浮点类型可以类似地转换为较大的类型.有符号和无符号整数类型可以相互转换.整数和浮点类型可以相互更改.

Integer types such as char and short can be converted to "greater" types such as int and long, and smaller floating-point types can similarly be converted into greater types. Signed and unsigned integer types can be converted into each other. Integer and floating-point types can be changed into each other.

由于C ++是一种OO语言,因此在许多类型转换中,基本和派生事项之间存在关系.在这里,了解实际对象,指针和引用之间的区别非常重要(特别是如果您来自.Net或Java).首先是实际对象.它们只有一种类型,您可以将它们转换为任何基本类型(暂时忽略私有基本类).转换将创建基本类型的 new 对象.我们称此为切片";将派生的部分切成薄片.

Since C++ is an OO language, there are a number of casts where the relation between Base and Derived matters. Here it is very important to understand the difference between actual objects, pointers, and references (especially if you're coming from .Net or Java). First, the actual objects. They have precisely one type, and you can convert them to any base type (ignoring private base classes for the moment). The conversion creates a new object of base type. We call this "slicing"; the derived parts are sliced off.

当您有指向对象的指针时,存在另一种类型的转换.您始终可以将 Derived * 转换为 Base * ,因为在每个Derived对象中都有一个Base子对象.C ++会自动将Base with Derived的正确偏移量应用于您的指针.这种转换将为您提供一个新的指针,但没有一个新的对象.新的指针将指向现有的子对象.因此,强制转换永远不会切掉对象的派生"部分.

Another type of conversion exists when you have pointers to objects. You can always convert a Derived* to a Base*, because inside every Derived object there is a Base subobject. C++ will automatically apply the correct offset of Base with Derived to your pointer. This conversion will give you a new pointer, but not a new object. The new pointer will point to the existing sub-object. Therefore, the cast will never slice off the Derived part of your object.

相反,转换比较棘手.通常,并非每个 Base * 都将指向Derived对象内的Base子对象.基础对象也可能存在于其他地方.因此,转换可能会失败.C ++在这里为您提供了两种选择.您要么告诉编译器您确定要通过 static_cast< Derived *>(baseptr)指向Derived中的子对象,要么要求编译器使用 dynamic_cast< Derived *>(baseptr).在后一种情况下,如果 baseptr 实际上没有指向派生对象,则结果将为 nullptr .

The conversion the other way is trickier. In general, not every Base* will point to Base sub-object inside a Derived object. Base objects may also exist in other places. Therefore, it is possible that the conversion should fail. C++ gives you two options here. Either you tell the compiler that you're certain that you're pointing to a subobject inside a Derived via a static_cast<Derived*>(baseptr), or you ask the compiler to check with dynamic_cast<Derived*>(baseptr). In the latter case, the result will be nullptr if baseptr doesn't actually point to a Derived object.

对于Base和Derived的引用,除 dynamic_cast< Derived&>(baseref)之外,该规定均适用:它将抛出 std :: bad_cast 而不返回空指针.(没有诸如空引用之类的东西.)

For references to Base and Derived, the same applies except for dynamic_cast<Derived&>(baseref) : it will throw std::bad_cast instead of returning a null pointer. (There are no such things as null references).

有两种定义用户转换的方法:通过源类型和通过目标类型.第一种方法涉及在源类型中定义成员 operator DestinatonType()const .请注意,它没有显式的返回类型(始终为 DestinatonType ),并且为 const .转换绝不能更改源对象.一个类可以定义几种可以将其转换为的类型,只需添加多个运算符即可.

There are two ways to define user conversions: via the source type and via the destination type. The first way involves defining a member operator DestinatonType() const in the source type. Note that it doesn't have an explicit return type (it's always DestinatonType), and that it's const. Conversions should never change the source object. A class may define several types to which it can be converted, simply by adding multiple operators.

第二种类型的转换(通过目标类型)依赖于用户定义的构造函数.可以使用一个类型为 U 的参数调用的构造方法 T :: T ,可以将 U 对象转换为T对象.该构造函数是否具有其他默认参数也无关紧要,U参数是通过值还是通过引用传递也无关紧要.但是,如前所述,如果 T :: T(U) explicit ,则在隐式转换序列中将不考虑它.

The second type of conversion, via the destination type, relies on user-defined constructors. A constructor T::T which can be called with one argument of type U can be used to convert a U object into a T object. It doesn't matter if that constructor has additional default arguments, nor does it matter if the U argument is passed by value or by reference. However, as noted before, if T::T(U) is explicit, then it will not be considered in implicit conversion sequences.

由于用户定义的转换序列,两种类型之间可能有多个转换序列.由于这些实质上是函数调用(对于用户定义的运算符或构造函数),因此转换顺序是通过不同函数调用的重载解析来选择的.

it is possible that multiple conversion sequences between two types are possible, as a result of user-defined conversion sequences. Since these are essentially function calls (to user-defined operators or constructors), the conversion sequence is chosen via overload resolution of the different function calls.

这篇关于C ++类型转换常见问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆