C++14中如何让编译器检测const引用传递constexpr变量的错误
我开发的仅头文件库需在C14(g)环境下定义float常量,库中代码如下:
struct A // POD with constants { static constexpr float a{0.1f}; // more stuff };
用户使用时编写函数声明:
void func(const float&);
并调用func(A::a);,会触发链接错误:
my.cpp:(.text+0xaffe): undefined reference to `A::a'
需要修改实现,让编译器(而非链接器)在将该constexpr变量作为const引用传递的位置抛出错误或警告。
背景补充
针对C14的MISRA安全编码规范已在去年年底发布,标准固定,无法切换到C17。当前可通过convert(A::a)规避错误,该函数定义为template<typename T> T convert(T v) {return v;};匿名枚举适用于整数类型,无需定义符号。嵌入式场景下库常以静态库交付,虽可通过测试用例检测错误,但问题仍常出现在集成阶段,希望通过编译器标志或模板结构体实现编译阶段检测,或直接消除符号需求。
1. 编译期强制报错:用不可取地址的包装类
修改库中常量的定义,用私有构造的包装类封装float常量,让用户无法直接将其绑定到const引用,触发编译错误:
struct A { struct ConstFloat { // 隐式转换为float,保留常量使用方式 constexpr operator float() const { return 0.1f; } private: // 私有构造,禁止创建可被取地址的左值 ConstFloat() = default; }; static constexpr ConstFloat a{}; // more stuff };
当用户调用func(A::a)时,编译器会直接报错,提示无法将ConstFloat类型绑定到const float&,将错误提前到编译阶段,避免链接期问题。用户若要正常使用,必须显式转换为float(如func(static_cast<float>(A::a))),此时会生成临时对象,无需访问A::a的存储,自然不会触发链接错误。
2. 消除链接错误:用静态成员函数替代常量
将constexpr常量改为返回值的静态成员函数,用户调用时会生成临时float对象,无需访问存储:
struct A { static constexpr float a() { return 0.1f; } // more stuff };
用户调用func(A::a())即可正常运行,编译器会优化掉函数调用,效果与直接使用常量一致,同时彻底避免链接错误。
3. 结合编译器警告与转换模板
保留原常量定义,同时在库中提供convert模板函数,并强制用户使用:
template<typename T> constexpr T convert(T v) { return v; } struct A { static constexpr float a{0.1f}; // more stuff };
要求用户通过func(convert(A::a))调用,模板会将常量值复制为临时对象,避免取地址需求。同时在编译时启用gcc的-Werror=address选项,当用户直接传递A::a时,编译器会因尝试取constexpr变量的地址(需类外定义)发出警告,转为错误后即可在编译期拦截。
内容的提问来源于stack exchange,提问作者Davidius

