auto 关键字
auto 在使用时非常方便,但是编译器自动判断的类型可能预期不符,例如 Eigen 在进行矩阵运算时,就应该尽可能避免使用 auto,否则会产生难以预期的结果。详见本博文。
函数返回值为智能指针
函数返回值为智能指针时,如果对返回值直接进行寻址并将寻址结果用于初始化引用,则会导致悬空引用的问题,例如:
std::shared_ptr<Typre> func();
const Type& tmp = *func();
这是因为对返回值寻址后,返回值智能指针的引用计数为0,空间被释放,从而导致悬空引用。
size_t 作为循环变量
size_t 通常用作存储容器尺寸的变量,但是该类型为无符号类型,因此在使用 size_t 作为循环变量时,需要额外注意避免出现负数情况,否则 size_t 会发生溢出。
inline 关键字的误导性
inline
关键字常被理解为将函数声明为内联函数。
但实际上,编译器并不会确保 inline
函数会被内联。
inline
的实际作用为向链接器声明该函数能够被重复定义,因此使得该函数能够被直接定义在头文件中,从而提高该函数被内联的可能性。
目前编译器会自动判断函数是否需要内联,通常不再需要手动设置 inline
。
类成员的初始化顺序
c++标准的12.6.2节的13.3指出:
Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
因此,在非委派构造函数中(delegating constructor),类中变量的初始化顺序为声明顺序,而与初始化列表的顺序无关。
模板超出最大递归限制
如果模板中定义了递归操作,需要额外注意模板递归的停止条件,例如下述代码:
template<int N>
int foo() {
if (N == 1) return 1;
return N + foo<N-1>();
}
上述代码想要利用模板求解1到N之和,但是编译器会给出模板实例化时超出限制的报错(template instantiation depth exceeds maximum of 900
)。这是由于在实例化 foo<N-1>
时,没有给定编译器的停止条件。
在 C++17
之前,上述问题可以通过特化模板来解决,如下述代码所示:
template<int N>
int foo() {
return N + foo<N-1>();
}
template<>
int foo<1>() {
return 1;
}
上述代码在 N = 1
时特化了模板,且该特化中不包含迭代,从而使得编译器可以在 N = 1
时停下。
在 C++17
之后,上述问题则可以使用 if constexpr
来解决,该指令用于在编译时判断分支是否应该被抛弃,代码如下:
template<int N>
int foo() {
if constexpr (N == 1) return 1;
else return N + foo<N-1>();
}
需要注意,else
在这里不能省略,因为 if constexpr
只能对于 if
和 else
两个分支进行判断,外部代码无法判断是否被抛弃。
单个参数包用作构造函数唯一参数时可能导致的问题
在使用参数包(parameter pack)作为构造函数的唯一参数时,默认拷贝构造函数在输入非const时会被其覆盖,可能导致一些难以排查的问题。
智能指针与普通指针的使用
智能指针的作用在于管理所有权,因此不涉及所有权的操作不应使用只能指针。
这里的所有权指的是能够决定指针指向的对象的生命周期的代码。
留下评论