如何提取并使用C++类的可变模板参数及其类型
C++可变模板参数重构测试夹具类的正确写法
问题背景
现有多个仅参数数量和类型不同的测试夹具类,希望用单个带可变模板参数的类替代,但现有可变模板写法存在语法错误,需正确提取并使用可变模板参数及其类型。
原多类实现代码
template <typename T1, typename T2> class SUTTestFixture1 { public: void SetUp(T1 expected, vector<T2> data) { _expected = expected; _data = data; } protected: SUT _sutObj; vector<T2> _data; T1 _expected; }; template <typename T1, typename T2, typename T3> class SUTTestFixture2 { public: void SetUp(T1 expected, T2 param, vector<T3> data) { _expected = expected; _param = param; _data = data; } protected: SUT _sutObj; vector<T3> _data; T1 _expected; T2 _param; };
错误的可变模板尝试写法
template <typename... T> class SUTTestFixture { public: void SetUp(T... args) { _expected = args...[0]; _data = args...[1]; // expected ‘;’ before ‘...’ token } protected: SUT _sutObj; vector<T...> _data; // XXX T... _expected; // error: expected unqualified-id before ‘...’ token };
错误原因分析
vector<T...> _data:vector仅接受单个元素类型,可变参数包T...无法直接作为其模板参数。T... _expected:C++不允许直接声明可变数量的成员变量,需用std::tuple等容器存储多参数。args...[0]:可变参数包不能直接通过索引访问,需通过参数拆分或std::tuple间接获取。
正确的可变模板实现方案
针对原夹具类的规律(第一个参数为预期值类型,最后一个为数据容器的元素类型,中间为可选的额外参数),采用模板参数拆分+std::tuple存储额外参数的方式实现通用夹具:
#include <tuple> #include <vector> template <typename ExpectedType, typename... ExtraParams, typename DataElementType> class SUTTestFixture { protected: SUT _sutObj; std::vector<DataElementType> _data; ExpectedType _expected; std::tuple<ExtraParams...> _extraParams; // 存储所有额外参数 public: void SetUp(ExpectedType expected, ExtraParams... extras, std::vector<DataElementType> data) { _expected = expected; _extraParams = std::make_tuple(extras...); _data = data; } // 获取指定索引的额外参数(索引从0开始) template <std::size_t N> auto getExtraParam() -> decltype(std::get<N>(_extraParams)) { return std::get<N>(_extraParams); } };
写法说明
- 模板参数分为三部分:
ExpectedType(预期值类型)、ExtraParams...(可选的额外参数类型)、DataElementType(数据容器的元素类型),完全匹配原夹具的参数逻辑。 - 用
std::tuple<ExtraParams...>存储所有额外参数,兼容任意数量的中间参数。 - 提供
getExtraParam<N>()方法,方便在测试类中获取指定位置的额外参数。
对应使用示例
对应原SUTTestFixture1的使用场景
class TestFixture1 : public SUTTestFixture<long, long>, public testing::TestWithParam<std::tuple<long, std::vector<long>>> { public: void SetUp() override { SUTTestFixture::SetUp(std::get<0>(GetParam()), std::get<1>(GetParam())); } long Function1Test() { return _sutObj.Function1(_data); } };
注:此处SUTTestFixture<long, long>中,ExpectedType=long,ExtraParams为空,DataElementType=long,匹配原SUTTestFixture1<long, long>的逻辑。
对应原SUTTestFixture2的使用场景
class TestFixture2 : public SUTTestFixture<size_t, size_t, size_t>, public testing::TestWithParam<std::tuple<size_t, size_t, std::vector<size_t>>> { public: void SetUp() override { SUTTestFixture::SetUp(std::get<0>(GetParam()), std::get<1>(GetParam()), std::get<2>(GetParam())); } size_t Function2Test() { // 通过getExtraParam获取额外参数 return _sutObj.Function2(getExtraParam<0>(), _data); } };
注:此处SUTTestFixture<size_t, size_t, size_t>中,ExpectedType=size_t,ExtraParams={size_t},DataElementType=size_t,匹配原SUTTestFixture2<size_t, size_t, size_t>的逻辑。
内容的提问来源于stack exchange,提问作者khteh
相关产品推荐
相关产品推荐

