You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何无法在构造函数中使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 00:04:59