最近遇到的一些坑(C++)

最近写一个多项式类希望尽可能使用Modern C++,但是在使用过程中遇到了一些坑,记录如下:

constexpr的坑

最近用constexpr希望多项式类可以在编译期直接求值计算,但是在使用过程中发现了一些问题

std::vector的使用

因为std::vector是动态分配内存的,而constexpr要求在编译期就能确定大小,所以std::vector是不被允许使用。可以尝试运行以下例子(为方便直接用结构体struct了)

#include <vector>

struct Base{
    std::vector<int> _vec;
    constexpr Base(int n):_vec(n){}
    constexpr Base(const std::initializer_list<int>& il):_vec(il){}
};

上述代码会直接报错,原因也很清楚,constexpr 构造函数调用非 constexpr 构造函数来初始化子对象。所以如果要实现一个可以动态扩容的多项式类,大概率是无法使用constexpr的。为保证需要在编译期可知,使用std::array可能更好。因此我继续写了一个可以保证编译期求值的类polynimaicl_C

多文件

本来是打算把多项式类的声明写在polynimial.h中,定义写在polynomial.cpp中,然后把polynomial.cpp编译成静态库 与main.cpp链接,在main.cpp调用多项式类的成员函数。但是在使用过程中发现了一些问题,CMake编写如下

add_library(polynomial-lib STATIC polynomial.cpp)
add_executable(main main.cpp)
target_link_libraries(main polynomial-lib)

当时CMake一直无法成功编译,一时以为是CMake编写有误,但是main.cpp中的报错信息却在constexpr polynimial_C poly1行上说constexpr对象必须为只读或者引用类型,并且在头文件中被用过的函数报出警告,表示该函数已被使用却从未定义,同时在build文件夹下也能找到编译好的静态库。所以CMake应该没问题,或者是问题出在了 LNK 阶段。但是从main.cpp看来应该不是。

其实当时想的太过于简单了,一个对象的成员是constexpr类型的话,那么编译器在main.cpp中应该就能找到该函数的定义,否则只能靠链接阶段,但显然,此时已经离开编译期了。所以我们的constexpr函数的定义也要同时写在头文件中,以使得main.cpp能找到该函数的定义。我也尝试过在编译时让polynimial.cppmain.cpp一起直接编译成一个可执行文件,但是依然无法解决constexpr的报错问题,也可能是才疏学浅,没法想到更多原因。CMake如下:

add_executable(main main.cpp polynomial.cpp)

内置print函数的坑

这个是我希望能让每个多项式类直接打印出多项式,但是却在polynimial_C这个类出现了问题,这个类可以支持编译期求值,内部使用了stsd::array,为保证编译期可知项数,使用了模板template<std::size_t SIZE>。虽然按照想法来说,print函数一定是一个运行时的函数,因为他调用的是std::cout,所以没有被声明为constexpr类型,同时还把实现写在了polynimial.cpp中,但是这个类算一个模板类,所以实现应该写入到头文件中,也就是print函数也应该写在头文件。