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 只能对于 ifelse 两个分支进行判断,外部代码无法判断是否被抛弃。

单个参数包用作构造函数唯一参数时可能导致的问题

在使用参数包(parameter pack)作为构造函数的唯一参数时,默认拷贝构造函数在输入非const时会被其覆盖,可能导致一些难以排查的问题。

智能指针与普通指针的使用

智能指针的作用在于管理所有权,因此不涉及所有权的操作不应使用只能指针。

这里的所有权指的是能够决定指针指向的对象的生命周期的代码。

留下评论