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

如何在if/switch语句中结合C++模板参数处理Vulkan描述符?

问题:C++模板中根据类型分支赋值Vulkan描述符结构体

我用C++结合Vulkan开发游戏引擎,正在重构代码提升可读性。我泛化了一个描述符结构体供多类对象继承:UBO/SSBO对象包含VkDescriptorBufferInfo类型成员,Texture对象则包含VkDescriptorImageInfo类型成员,这两种类型都需要用来填充VkWriteDescriptorSet结构体,其定义如下:

typedef struct VkWriteDescriptorSet {
    VkStructureType                  sType;
    const void*                      pNext;
    VkDescriptorSet                  dstSet;
    uint32_t                         dstBinding;
    uint32_t                         dstArrayElement;
    uint32_t                         descriptorCount;
    VkDescriptorType                 descriptorType;
    const VkDescriptorImageInfo*     pImageInfo;   // Texture对象提供
    const VkDescriptorBufferInfo*    pBufferInfo;  // UBO/SSBO对象提供
    const VkBufferView*              pTexelBufferView;
} VkWriteDescriptorSet;

我尝试编写模板函数统一创建VkWriteDescriptorSet:

template <typename T>
inline VkWriteDescriptorSet writeSet(const std::vector<T>& bufferInfo, std::array<size_t,2> dst) {
    VkWriteDescriptorSet writeInfo { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };

    if (std::is_same<T, VkDescriptorImageInfo>::value) {
        writeInfo.pImageInfo = &bufferInfo[dst[1]];  // 仅T为VkDescriptorImageInfo时合法
    }
    else {
        writeInfo.pBufferInfo = &bufferInfo[dst[1]]; // 仅T为VkDescriptorBufferInfo时合法
    }

    // 填充其他字段...

    return writeInfo;
}

但编译时报错:

Severity: Error
Line: 27
Code: C2440
Description: '=': cannot convert from 'const _Ty *' to 'const VkDescriptorImageInfo *'

之后我尝试用VkDescriptorType枚举做switch分支,同样遇到类型转换错误:

template <typename T>
inline VkWriteDescriptorSet writeSet(const std::vector<T>& bufferInfo, std::array<size_t,2> dst) {
    VkWriteDescriptorSet writeInfo { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };

    switch (type) {
    case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
        writeInfo.pImageInfo = &bufferInfo[dst[1]];  // 仅T=VkDescriptorImageInfo合法
    default:
        writeInfo.pBufferInfo = &bufferInfo[dst[1]]; // 仅T=VkDescriptorBufferInfo合法
    }

    // 填充其他字段...

    return writeInfo;
}

需要解决的核心问题:如何在模板函数中根据类型或枚举正确分支,避免类型转换错误?


错误原因

普通if或switch属于运行时分支,编译器会检查所有分支的代码合法性,不管条件是否为真。比如当T是VkDescriptorBufferInfo时,writeInfo.pImageInfo = &bufferInfo[dst[1]]这行代码仍会被编译,此时const VkDescriptorBufferInfo*无法转换成const VkDescriptorImageInfo*,直接触发编译错误。


解决方案

1. 模板特化

直接为两种目标类型分别实现模板函数,编译器会根据传入的T自动匹配对应的特化版本:

// 基础模板(留空或添加断言,限制仅支持指定类型)
template <typename T>
inline VkWriteDescriptorSet writeSet(const std::vector<T>& bufferInfo, std::array<size_t,2> dst);

// VkDescriptorImageInfo的特化实现
template <>
inline VkWriteDescriptorSet writeSet<VkDescriptorImageInfo>(const std::vector<VkDescriptorImageInfo>& bufferInfo, std::array<size_t,2> dst) {
    VkWriteDescriptorSet writeInfo { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
    writeInfo.pImageInfo = &bufferInfo[dst[1]];
    // 填充其他字段...
    return writeInfo;
}

// VkDescriptorBufferInfo的特化实现
template <>
inline VkWriteDescriptorSet writeSet<VkDescriptorBufferInfo>(const std::vector<VkDescriptorBufferInfo>& bufferInfo, std::array<size_t,2> dst) {
    VkWriteDescriptorSet writeInfo { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
    writeInfo.pBufferInfo = &bufferInfo[dst[1]];
    // 填充其他字段...
    return writeInfo;
}

2. C++17 constexpr if

使用constexpr if实现编译时分支,编译器会根据模板参数T的类型,仅编译符合条件的分支,忽略另一分支的代码:

template <typename T>
inline VkWriteDescriptorSet writeSet(const std::vector<T>& bufferInfo, std::array<size_t,2> dst) {
    VkWriteDescriptorSet writeInfo { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };

    if constexpr (std::is_same_v<T, VkDescriptorImageInfo>) {
        writeInfo.pImageInfo = &bufferInfo[dst[1]];
    }
    else if constexpr (std::is_same_v<T, VkDescriptorBufferInfo>) {
        writeInfo.pBufferInfo = &bufferInfo[dst[1]];
    }
    else {
        // 非法类型触发编译断言
        static_assert(false, "不支持的描述符信息类型");
    }

    // 填充其他字段...

    return writeInfo;
}

3. 标签分发(Tag Dispatch)

利用重载函数和标签类,让编译器在编译时自动选择正确的重载实现:

// 定义标签类
struct ImageInfoTag {};
struct BufferInfoTag {};

// 为每种类型绑定对应的标签
template <typename T>
struct DescriptorTag;

template <>
struct DescriptorTag<VkDescriptorImageInfo> { using type = ImageInfoTag; };

template <>
struct DescriptorTag<VkDescriptorBufferInfo> { using type = BufferInfoTag; };

// 针对不同标签的重载实现
inline VkWriteDescriptorSet writeSetImpl(const std::vector<VkDescriptorImageInfo>& bufferInfo, std::array<size_t,2> dst, ImageInfoTag) {
    VkWriteDescriptorSet writeInfo { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
    writeInfo.pImageInfo = &bufferInfo[dst[1]];
    // 填充其他字段...
    return writeInfo;
}

inline VkWriteDescriptorSet writeSetImpl(const std::vector<VkDescriptorBufferInfo>& bufferInfo, std::array<size_t,2> dst, BufferInfoTag) {
    VkWriteDescriptorSet writeInfo { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
    writeInfo.pBufferInfo = &bufferInfo[dst[1]];
    // 填充其他字段...
    return writeInfo;
}

// 对外暴露的模板接口
template <typename T>
inline VkWriteDescriptorSet writeSet(const std::vector<T>& bufferInfo, std::array<size_t,2> dst) {
    using Tag = typename DescriptorTag<T>::type;
    return writeSetImpl(bufferInfo, dst, Tag{});
}

结合VkDescriptorType枚举的分支优化

如果需要用VkDescriptorType枚举分支,需确保枚举值与模板参数T强绑定,可通过静态断言避免类型不匹配:

template <typename T>
inline VkWriteDescriptorSet writeSet(const std::vector<T>& bufferInfo, std::array<size_t,2> dst, VkDescriptorType descriptorType) {
    VkWriteDescriptorSet writeInfo { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
    writeInfo.descriptorType = descriptorType;

    if constexpr (std::is_same_v<T, VkDescriptorImageInfo>) {
        static_assert(
            descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || 
            descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
            "VkDescriptorImageInfo与传入的描述符类型不匹配"
        );
        writeInfo.pImageInfo = &bufferInfo[dst[1]];
    }
    else if constexpr (std::is_same_v<T, VkDescriptorBufferInfo>) {
        static_assert(
            descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || 
            descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
            "VkDescriptorBufferInfo与传入的描述符类型不匹配"
        );
        writeInfo.pBufferInfo = &bufferInfo[dst[1]];
    }
    else {
        static_assert(false, "不支持的描述符信息类型");
    }

    // 填充其他字段...

    return writeInfo;
}

内容的提问来源于stack exchange,提问作者ModernEraCaveman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 05:30:14