为何无法在构造函数中使用QString参数包?C++模板类问题
问题与解决方案
问题描述
定义了如下用于遥测数据位压缩的C++模板类:
template <size_t... Bits> class TelemetryCompactDataStrategy : public TelemetryCommonDataStrategy { static_assert((Bits + ...) <= 32, "Bit count must not exceed 32."); public: static constexpr auto bitSize = (Bits + ...); static constexpr auto dataSize = (bitSize + 7) / 8; TelemetryCompactDataStrategy(QString... paths, QObject* parent = nullptr) : TelemetryCommonDataStrategy({paths, ...}, dataSize, parent), m_args({paths, ...}) { static_assert(sizeof...(Bits) == sizeof...(paths), "Number of bits and values must match."); } QByteArray toByteArray() override { uint32_t value = 0; currentIndex = 0; writeBits<0>(value, m_values); QByteArray byteArray; byteArray.append(reinterpret_cast<const char*>(&value), dataSize); return (dataSize == m_dataSize) ? byteArray : QByteArray(m_dataSize, 0x00); } int validTelemetryDataCount() override { return m_args.size(); } QVariantHash toVariantHash(QByteArray datas) override { return {}; } protected: void processedTelemetryValue() override { m_values = m_rawValues; } private: std::array<QString, sizeof...(Bits)> m_args; size_t currentIndex = 0; template <size_t Index> void writeBits(uint32_t& value, const QMap<QString, QVariant>& values) { constexpr size_t BitCount = BitsAtIndex<Index>(); if (currentIndex + BitCount > 32) throw std::runtime_error("Bit count exceeds 32."); const auto valueToAdd = values[m_args[Index]].toInt() & ((1 << BitCount) - 1); value |= (valueToAdd << currentIndex); currentIndex += BitCount; if constexpr (Index + 1 < sizeof...(Bits)) writeBits<Index + 1>(value, values); } template <size_t Index> static constexpr size_t BitsAtIndex() { if constexpr (Index < sizeof...(Bits)) return Bits + ... + (Index * 0); // Force evaluation of Bits at Index else return 0; } };
尝试按以下方式实例化时失败:
new TelemetryCompactDataStrategy<2, 1, 1, 2, 1>(getTelemetryDataInfo("LowBeamLight").path, getTelemetryDataInfo("RainLight").path, getTelemetryDataInfo("Wipers").path, getTelemetryDataInfo("EngineIgnition").path, getTelemetryDataInfo("TyreType").path, this)
核心疑问:
- 为何无法直接用
QString...作为构造函数参数? - 如何修改实现支持多比特配置的遥测数据压缩类?
解决方案
1. 修复构造函数参数顺序问题
C++中,模板参数包对应的非模板参数包不能跟在有默认值的参数后面,编译器无法正确划分参数边界。有两种修复方式:
方案A:调整参数顺序
将带默认值的QObject* parent移到参数包前面:
TelemetryCompactDataStrategy(QObject* parent = nullptr, QString... paths) : TelemetryCommonDataStrategy({paths, ...}, dataSize, parent), m_args({paths, ...}) { static_assert(sizeof...(Bits) == sizeof...(paths), "Number of bits and values must match."); }
实例化时对应调整顺序:
new TelemetryCompactDataStrategy<2,1,1,2,1>(this, getTelemetryDataInfo("LowBeamLight").path, getTelemetryDataInfo("RainLight").path, getTelemetryDataInfo("Wipers").path, getTelemetryDataInfo("EngineIgnition").path, getTelemetryDataInfo("TyreType").path)
方案B:使用std::initializer_list<QString>
用初始化列表接收路径,代码可读性更强,也避免了参数顺序问题:
TelemetryCompactDataStrategy(std::initializer_list<QString> paths, QObject* parent = nullptr) : TelemetryCommonDataStrategy(paths, dataSize, parent), m_args(paths.begin(), paths.end()) { static_assert(sizeof...(Bits) == paths.size(), "Number of bits and values must match."); }
实例化时用大括号包裹路径参数:
new TelemetryCompactDataStrategy<2,1,1,2,1>({ getTelemetryDataInfo("LowBeamLight").path, getTelemetryDataInfo("RainLight").path, getTelemetryDataInfo("Wipers").path, getTelemetryDataInfo("EngineIgnition").path, getTelemetryDataInfo("TyreType").path }, this)
2. 修复BitsAtIndex函数的逻辑错误
原BitsAtIndex的写法return Bits + ... + (Index * 0);完全错误,无法获取对应索引的比特数。以下是两种正确实现:
方式1:C++17及以上(用std::array折叠)
template <size_t Index> static constexpr size_t BitsAtIndex() { constexpr std::array<size_t, sizeof...(Bits)> bitsArray = {Bits...}; static_assert(Index < bitsArray.size(), "Index out of range."); return bitsArray[Index]; }
方式2:递归模板(兼容更早C++版本)
template <size_t Index, size_t First, size_t... Rest> struct GetBitCount { static constexpr size_t value = GetBitCount<Index - 1, Rest...>::value; }; template <size_t First, size_t... Rest> struct GetBitCount<0, First, Rest...> { static constexpr size_t value = First; }; template <size_t Index> static constexpr size_t BitsAtIndex() { static_assert(Index < sizeof...(Bits), "Index out of range."); return GetBitCount<Index, Bits...>::value; }
3. 补充说明
- 可以直接用具体类型(如
QString)作为参数包元素,并非只能用模板类型参数T...,问题出在参数顺序和编译器推导逻辑,而非类型本身。 - 对于固定数量的同类型参数,
std::initializer_list比参数包更合适,代码更易读,也能避免参数边界推导问题。
内容的提问来源于stack exchange,提问作者wcc chn
相关产品推荐
相关产品推荐

