如何在C++类中声明类型推导的成员变量?
在C++类中声明无需显式指定类型的成员变量
在函数中可以用auto轻松声明Wrapper<lambda类型>或Wrapper<成员函数指针类型>这类变量,但将它们作为类成员时,会遇到类型难以显式写出(如成员函数指针)或类型无法直接引用(如lambda的匿名类型)的问题。以下是尝试过但无法生效的代码:
template<typename T> class Wrapper { T t; public: explicit Wrapper(T t) : t(t) {} void call() { t(); }; }; class Problem { public: void test() {} // auto x = Wrapper([](auto x) {}); // 1. 无法生效:lambda类型匿名,类内auto成员推导受限 // auto y = Wrapper(&Problem::test); // 2. 无法生效:类内成员不能直接用auto推导类型 // Wrapper<void (Problem::*)()> y = Wrapper(&Problem::test); // 3. 可行但类型太复杂,不愿手动编写 // static auto getter(){ // return Wrapper(&Problem::test); // } // decltype(getter()) z = getter(); // 4. 无法生效:auto返回类型的函数在类内推导未完成 }; int main() { auto x = Wrapper([] {}); auto y = Wrapper(&Problem::test); Problem p; }
针对成员函数指针包装的情况(如示例中的y)
方案1:用decltype简化类型书写
不需要手动写出复杂的成员函数指针类型,借助decltype自动推导目标类型:
class Problem { public: void test() {} // 用decltype推导成员函数指针类型,避免手动编写冗长类型 Wrapper<decltype(&Problem::test)> y = Wrapper(&Problem::test); };
方案2:修正静态成员函数+decltype的写法
将静态成员函数的声明与定义分离,确保decltype能正确推导返回类型:
class Problem { public: void test() {} // 先声明静态函数 static auto getter(); // 用decltype推导函数返回类型作为成员变量类型 decltype(getter()) z; // 在构造函数中完成初始化 Problem() : z(getter()) {} }; // 类外定义静态函数,此时编译器能推导auto返回类型 auto Problem::getter() { return Wrapper(&Problem::test); }
方案3:C++17及以上使用inline auto成员变量
C++17允许inline非静态成员变量通过类内初始化器用auto推导类型:
class Problem { public: void test() {} // C++17及以上生效,auto自动推导Wrapper的实例类型 inline auto y = Wrapper(&Problem::test); };
针对lambda包装的情况(如示例中的x)
lambda的类型是匿名且唯一的,无法直接在类内声明对应成员变量,可通过以下方式解决:
方案1:C++20及以上使用constexpr lambda+inline auto
C++20支持带auto参数的constexpr lambda,结合inline auto可直接声明成员变量:
class Problem { public: // C++20及以上生效,constexpr lambda支持auto参数,inline auto推导类型 inline auto x = Wrapper([](auto x) {}); };
方案2:将lambda作为类模板参数传递
提前定义lambda,将其类型作为类模板参数,在类内声明对应成员:
// 提前定义constexpr lambda(C++17及以上支持constexpr lambda) constexpr auto my_lambda = [](auto x) {}; // 将lambda类型作为模板参数传入Problem类 template<typename Lambda = decltype(my_lambda)> class Problem { public: Wrapper<Lambda> x = Wrapper(my_lambda); };
方案3:使用std::function(带性能开销)
如果可以接受运行时多态的性能损耗,用std::function封装调用逻辑,避开lambda的匿名类型问题:
#include <functional> template<typename T> class Wrapper { std::function<void(T)> t; public: explicit Wrapper(std::function<void(T)> t) : t(t) {} void call(T arg) { t(arg); }; }; class Problem { public: // 用std::function<void(int)>封装lambda,无需关心lambda的具体类型 Wrapper<int> x = Wrapper([](auto x) { /* 执行逻辑 */ }); };
内容的提问来源于stack exchange,提问作者gerum
相关产品推荐
相关产品推荐

