Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint

C++ has from the beginning attempted to improve on the type system of C, adding features like classes that let you build better types and enums, which eliminate the need for some uses of the preprocessor (which is not at all type safe). C++ also performs fewer implicit type conversions for you (such as not allowing implicit assignment from void*), letting the compiler find more bugs for you.

C++11 goes even further. Even though enums got rid of the need for integer #define constants, we still had the ugly, poorly typed NULL pointer. C++11 cleans this up by adding an explicit, clear nullptr value with its own type. C++11 also brings new, strongly typed enums. In this article, I'll cover both these improvements.

Why do we need strongly typed enums?

So why do we need strongly typed enums anyway? Old-style C++ enums are essentially integers; they could be compared with integers or with other enums of different types. The thing is, you normally don't want to do that since enums are supposed to be some fixed list of enumerated values. Why would you want to compare to some other enum type (or an integer)? It's like saying, "please compare this kind of nail with this kind of toothbrush." It makes no sense, and you probably don't mean to do it. But old-style C++ enums will happily tell you, "why yes, this nail isn't like this toothbrush" or, worse, they might compare equal because they happen to share the same underlying integer value ("ah yes, this nail IS a Panasonic electronic toothbrush"). Now, with strongly typed enums, the compiler will tell you that you're doing it. If you really mean it, you can always use a typecast.

Another limitation is that enum values were unscoped--in other words, you couldn't have two enumerations that shared the same name:

// this code won't compile!
enum Color {RED, GREEN, BLUE}; 
enum Feelings {EXCITED, MOODY, BLUE};

Strongly typed enums - enum classes

Enter strongly typed enums--and I don't mean enums. Strongly typed enums are a new kind of enum, declared like so:

// this code will compile (if your compiler supports C++11 strongly typed enums)
enum class Color {RED, GREEN, BLUE}; 
enum class Feelings {EXCITED, MOODY, BLUE};

The use of the word class is meant to indicate that each enum type really is different and not comparable to other enum types. Strongly typed enums, enum classes, also have better scoping. Each enum value is scoped within the name of the enum class. In other words, to access the enum values, you must write:

Color color = Color::GREEN;
if ( Color::RED == color )
{
    // the color is red
}

Old style C++ enums are still available--if you want them--largely for backward compatibility with existing code bases. They did pick up one trick; you can now, optionally, put the enum name in front of the value: Color::RED. But since this is optional, it doesn't solve any naming conflicts; it just makes it a little bit more clear.

Enum classes have another advantages over old-style enums. You can have a forward declaration to a strongly typed enum, meaning that you can write code like:

enum class Mood;

void assessMood (Mood m);
 
// later on:
enum class Mood { EXCITED, MOODY, BLUE };

Why would this be useful? Forward declarations are often about the physical layout of code on disk into different files or to provide opaque objects as part of an API. In the first case, where you care about the physical disk layout, using a forward declaration allows you to declare an enum type in the header file while putting specific values into the cpp file. This lets you change the list of possible enum values quite frequently without forcing all dependent files to recompile. In the second case, an enum class can be exposed as a type-safe but otherwise opaque value returned from one API function to be passed into another API function. The code using the API need not know the possible values the type can take on. Since the compiler still knows about the type, it can enforce that variables declared to work with that type are not confused with variables working with another type.

Well-defined enum sizes

A final advantage of enum classes is that you can set the size of your enum--you can use any signed or unsigned integer type. It defaults to int, but you can also use char, unsigned long, etc. This will ensure some measure of compatibility across compilers.

// we only have three colors, so no need for ints!
enum class Colors : char { RED = 1, GREEN = 2, BLUE = 3 };

But in C++11, we can do even better, specifying exact sizes for enums, using cstdint.

<cstdint>

One problem that C++ has suffered from is a lack of standard types that provide fixed, well-defined sizes. For example, sometimes you want to have a 32-bit integer, not just an int that might have different sizes on different architectures. In C++11, the C99 header file stdint.h has been included as cstdint. The cstdint header includes types such as std::int8_t, std::int16_t, std::int32_t, and std::int64_t (as well as unsigned versions that begin with u: std::uint8_t).

Here's an example that combines these new types with enum classes to get completely known sizes for your enum across compilers and architectures:

#include <cstdint>
enum class Colors : std::int8_t { RED = 1, GREEN = 2, BLUE = 3 };

nullptr

In C and C++, it's always been important to express the idea of a NULL pointer--one that has no value. Oddly, in C++, the expression used, 0 (or NULL, always #defined to zero) was not even a pointer type. Although this worked most of the time, it could lead to strange and unexpected problems in what are, admittedly, rather edge cases. For example imagine you have the following two function declarations:

void func(int n); 
void func(char *s);

func( NULL ); // guess which function gets called?

Although it looks like the second function will be called--you are, after all, passing in what seems to be a pointer--it's really the first function that will be called! The trouble is that because NULL is 0, and 0 is an integer, the first version of func will be called instead. This is the kind of thing that, yes, doesn't happen all the time, but when it does happen, is extremely frustrating and confusing. If you didn't know the details of what is going on, it might well look like a compiler bug. A language feature that looks like a compiler bug is, well, not something you want.

Enter nullptr. In C++11, nullptr is a new keyword that can (and should!) be used to represent NULL pointers; in other words, wherever you were writing NULL before, you should use nullptr instead. It's no more clear to you, the programmer, (everyone knows what NULL means), but it's more explicit to the compiler, which will no longer see 0s everywhere being used to have special meaning when used as a pointer.

std::nullptr_t

nullptr, by the way, is not only declared to be a pointer and convert implicitly to all pointer types (and bool), but it is its own special, distinct type:

decltype( nullptr )

While we can use decltype to extract its type, there is also a more convenient notation:

std::nullptr_t

Since nullptr is its own unique type, you can use it as a constructor or function argument when you want to be sure that you only ever take an empty pointer for a value. For example:

void func( std::nullptr_t );

declares a function that takes only nullptr (or a value cast to std::nullptr_t) and nothing else, a rather neat trick.

Regardless of all this--the rule of thumb for C++11 is simply to start using nullptr whenever you would have otherwise used NULL in the past.

Previous: Faster Code with Rvalue References and Move Semantics  Learn how C++11 lets you write faster code in yet another way, by avoiding slow copies in favor of fast moves
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值