如何通过模板访问结构体数据成员?C++代码异常输出问题排查
问题:修正基于类成员指针的模板类逻辑错误
需求是实现一个模板类,以类的成员指针作为模板参数,传入类实例后能对指定成员执行操作。现有代码输出不符合预期,具体代码与错误输出如下:
原代码
#include <functional> #include <iostream> struct B { int foo; bool bar; }; template<auto M> class A { public: typedef std::function<bool(decltype(std::declval<B>().*M)&)> Callback; A(Callback callback) : m_callback(callback) {} bool X ( B& b ) const { if(m_callback(m_cache)) { b.*M = m_cache; return true; } return false; } protected: Callback m_callback; mutable typename std::remove_cvref<decltype(std::declval<B>().*M)>::type m_cache; }; using FooA = A<&B::foo>; using BarA = A<&B::bar>; int main() { FooA fooA([](int& f) { std::cout << "foo " << f << std::endl; ++f; return true; }); BarA barA([](bool& b) { std::cout << "bar " << b << std::endl; b = !b; return true; }); B b{.foo = 1, .bar = false}; fooA.X(b); barA.X(b); fooA.X(b); barA.X(b); return 0; };
错误输出
foo 0 bar 120 foo 1 bar 121
问题分析
- 未初始化的
m_cache:m_cache是mutable成员,未在构造阶段赋值,初始值为内存垃圾值,导致第一次输出异常。 - 逻辑顺序错误:当前代码直接修改
m_cache后赋值给b的成员,但正确流程应该是先将b的目标成员值读取到m_cache,再通过回调修改m_cache,最后写回b的成员。
修正后的代码
#include <functional> #include <iostream> struct B { int foo; bool bar; }; template<auto M> class A { public: using Callback = std::function<bool(decltype(std::declval<B>().*M)&)>; explicit A(Callback callback) : m_callback(std::move(callback)) {} bool X(B& b) const { // 先将b的目标成员值同步到cache m_cache = b.*M; if (m_callback(m_cache)) { // 将修改后的cache写回b的成员 b.*M = m_cache; return true; } return false; } protected: Callback m_callback; mutable std::remove_cvref_t<decltype(std::declval<B>().*M)> m_cache; }; using FooA = A<&B::foo>; using BarA = A<&B::bar>; int main() { FooA fooA([](int& f) { std::cout << "foo " << f << std::endl; ++f; return true; }); BarA barA([](bool& b) { std::cout << "bar " << b << std::endl; b = !b; return true; }); B b{.foo = 1, .bar = false}; fooA.X(b); barA.X(b); fooA.X(b); barA.X(b); return 0; };
修正说明
- 修复
m_cache的初始值问题:在X函数开头先同步b的目标成员值到m_cache,确保回调操作的是当前实例的真实数据,而非内存垃圾值。 - 简化类型声明:用
std::remove_cvref_t替代typename std::remove_cvref<...>::type(C++14及以上支持),代码更简洁。 - 构造函数优化:使用
std::move转移回调对象所有权,避免不必要的拷贝;添加explicit关键字防止隐式转换,提升代码安全性。
正确输出
foo 1 bar 0 foo 2 bar 1
内容的提问来源于stack exchange,提问作者D.G. Redd
相关产品推荐
相关产品推荐

