C++类模板无开销实现多个可选成员的可行方案咨询
方案实现
你可以通过空基类优化(EBCO) 实现需求,代码量仅和可选成员数量线性相关,无额外运行时开销和内存浪费。
C++11 兼容实现方案
首先定义通用的可选成员基类模板:
#include <type_traits> #include <iostream> #include <utility> using namespace std; // 通用模板:非void类型持有成员 template<class T, int Tag> struct OptionalMember { T val; }; // void特化:空类,不占用内存 template<int Tag> struct OptionalMember<void, Tag> {};
Tag参数用于区分相同类型的多个可选成员,避免基类冲突。接下来继承多个OptionalMember实例实现你的类:
template<class T1, class T2, class T3> struct X : private OptionalMember<T1, 1>, private OptionalMember<T2, 2>, private OptionalMember<T3, 3> { void print() { if constexpr (!is_void_v<T1>) { cout << "t1 is " << OptionalMember<T1, 1>::val << '\n'; } if constexpr (!is_void_v<T2>) { cout << "t2 is " << OptionalMember<T2, 2>::val << '\n'; } if constexpr (!is_void_v<T3>) { cout << "t3 is " << OptionalMember<T3, 3>::val << '\n'; } } // 赋值/访问接口示例,按需扩展 template<class T> void set(T&& value) { if constexpr (is_same_v<std::decay_t<T>, T1>) { OptionalMember<T1, 1>::val = forward<T>(value); } else if constexpr (is_same_v<std::decay_t<T>, T2>) { OptionalMember<T2, 2>::val = forward<T>(value); } else if constexpr (is_same_v<std::decay_t<T>, T3>) { OptionalMember<T3, 3>::val = forward<T>(value); } } };
该方案利用空基类优化规则,所有void特化的OptionalMember基类都不会占用类对象的内存空间,完全无额外内存开销。你可以通过增加OptionalMember的继承数量扩展可选成员,代码量仅随成员数量线性增长。
C++20 简化实现方案
如果可以使用C++20,可以用[[no_unique_address]]属性进一步简化代码,不需要继承即可实现零开销:
template<class T> struct OptionalField { T val; }; template<> struct OptionalField<void> {}; template<class T1, class T2, class T3> struct X { [[no_unique_address]] OptionalField<T1> t1; [[no_unique_address]] OptionalField<T2> t2; [[no_unique_address]] OptionalField<T3> t3; void print() { if constexpr (!is_void_v<T1>) cout << "t1 is " << t1.val << '\n'; if constexpr (!is_void_v<T2>) cout << "t2 is " << t2.val << '\n'; if constexpr (!is_void_v<T3>) cout << "t3 is " << t3.val << '\n'; } };
[[no_unique_address]]会提示编译器允许该成员和其他非同名成员共享内存地址,当OptionalField是void特化的空类时,不会占用任何内存空间。
效果验证
你可以通过sizeof关键字验证内存占用:
// 三个成员都为void时,空类大小为1(仅C++空类占位要求,无实际内存开销) static_assert(sizeof(X<void, void, void>) == 1); // 仅一个int成员时,大小等于int的大小 static_assert(sizeof(X<int, void, void>) == sizeof(int)); // 两个成员int和double时,大小等于int+double对齐后的大小 static_assert(sizeof(X<int, double, void>) == sizeof(int) + sizeof(double));
所有场景都无额外内存浪费,运行时所有分支都是编译期确定,无额外运行时开销。
内容的提问来源于stack exchange,提问作者igel
相关产品推荐
相关产品推荐

