如何测试const成员函数?实现无需注释测试代码的编译验证
目标
对const下标运算符进行单元测试并通过测试。即能否通过让编译器抛出错误来验证功能成功,同时无需注释该测试代码,以保证其余测试可正常编译?
尝试方案
我的思路是,若检测到is_const<>::value为true,则抛出异常,让单元测试捕获该异常,以此证明无法修改下标索引处元素的值,从而使测试“通过”。
我的const下标运算符定义
#include <type_traits> // operator [] const template <typename T> typename Vector<T>::const_reference Vector<T>::operator[](size_t index) const { if (size_ <= index || index < 0) { throw std::out_of_range("Index out of range."); } if constexpr(std::is_const<std::decltype(*this)>::value) { throw std::runtime_error("Cannot modify elements of a const Vector."); } return array_[index]; }
我的单元测试代码
TEST(SubScriptConst) { const Vector<int> myVecConst = {0,1,2,3,4,5,6,7,8}; try { myVecConst[2] = 4; // This will throw an exception? } catch (const std::exception& ex) { std::cout << "SUCCESS | Caught exception: " << ex.what() << std::endl; } CHECK_EQUAL(myVecConst[2], 2); }
明显问题
该方案无法运行。我知道这是因为对C++语言理解不足。我的推测是编译器会在代码执行前检查const对象的赋值操作并报错。我希望能将try块放在#ifdef测试宏中,仅在测试时启用,非测试时仍让编译器抛出错误。
解决方案
核心问题分析
你当前的思路存在本质错误:const成员函数返回const_reference时,myVecConst[2] = 4是编译期错误,根本到不了运行时抛异常的阶段——编译器会直接阻止这种修改const对象的行为,所以你的try-catch完全没机会执行。
而且你在const版operator[]里加的if constexpr判断也是多余的:*this在const成员函数里本身就是const类型,这个判断永远为true,会导致每次调用const下标运算符都抛异常,这显然不符合预期(const下标应该允许读取,只是禁止修改)。
正确的测试方式
要验证const下标运算符的正确性,分两部分:
- 验证读取功能正常:这部分就是你已有的
CHECK_EQUAL(myVecConst[2], 2);,直接测读取值即可。 - 验证无法修改:这部分需要在编译期验证,而不是运行时。可以通过以下方式实现:
编译期验证方案(无需注释代码)
借助C++的SFINAE特性,编写一个检测是否能赋值的辅助模板,通过静态断言验证结果:
#include <type_traits> // 辅助模板:检测类型是否支持赋值操作 template<typename T, typename = void> struct is_assignable : std::false_type {}; template<typename T> struct is_assignable<T, std::void_t<decltype(std::declval<T>() = std::declval<T>())>> : std::true_type {}; TEST(SubScriptConst_CannotModify) { const Vector<int> myVecConst = {0,1,2,3,4,5,6,7,8}; // 编译期验证:const下标返回值不可赋值 static_assert(!is_assignable<decltype(myVecConst[2])>::value, "Const Vector's operator[] should return non-assignable const reference"); // 读取功能验证 CHECK_EQUAL(myVecConst[2], 2); }
这种方式完全在编译期完成验证,不需要注释任何代码,且非测试环境下如果有人尝试修改const Vector的元素,编译器依然会直接报错。
修正你的const下标运算符
首先要删掉多余的if constexpr判断,同时移除冗余的index <0判断(size_t是无符号类型,永远不会小于0):
#include <type_traits> // operator [] const template <typename T> typename Vector<T>::const_reference Vector<T>::operator[](size_t index) const { if (index >= size_) { throw std::out_of_range("Index out of range."); } return array_[index]; }
内容的提问来源于stack exchange,提问作者FredFrugal

