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

C++模板元编程中value_pack转type_pack的MSVC编译异常问题

MSVC下非类型模板参数的模板模板参数编译问题

问题描述

开发类型打包、值打包的模板元编程库时,遇到跨编译器兼容问题:代码在GCC 12.2.0(C++20标准)下能正常运行,但在MSVC 19.29.30151(用/std:c++20编译)时抛出错误C2993: 'auto': is not a valid type for non-type template parameter '__formal'。

这段代码的核心意图是实现别名模板transform_to_type,它接收一个带有type类型定义的元函数,把value_pack里的所有值转换为对应的类型,打包成type_pack。临时改成using transform_to_type = type_pack<decltype(typename meta_transformer<values>::type())...>;能让MSVC编译通过,但这种写法并不理想。

需要解决三个问题:

  • 这是不是MSVC的编译器bug?
  • 怎么让原代码在MSVC上正常工作?
  • 有没有其他兼容MSVC的实现方式?

原代码

#include <type_traits>

template <typename T>
struct type_identity {
    using type = T;
};

template <typename ...types>
struct type_pack {};

template <auto ...values>
struct value_pack {
  template <template <auto> typename meta_transformer>
  using transform_to_type = type_pack<typename meta_transformer<values>::type...>; //C2993 on MSVC
};

template <auto i>
struct type_map;

template <>
struct type_map<1> : type_identity<int> {};

template <>
struct type_map<2.0> : type_identity<double> {};

template <>
struct type_map<'c'> : type_identity<char> {};

int main() {
    using mapped_types = value_pack<1, 2.0, 'c'>::transform_to_type<type_map>;
    static_assert(std::is_same_v<mapped_types, type_pack<int, double, char>>, "Error!");
    return 0;
}

问题解答

1. 是否为MSVC编译器bug?

是,这属于MSVC对C20标准支持不到位的兼容性bug。根据C20标准,模板模板参数允许用auto作为非类型模板参数的占位符,GCC、Clang都已经正确实现了这个特性,但MSVC 19.29这类旧版本还没完全支持。升级到Visual Studio 2022 17.0及以上版本(对应MSVC 19.30+)就能解决这个问题。

2. 让原代码在MSVC 19.29正常工作的方法

可以通过显式指定模板模板参数的非类型参数类型占位符,适配MSVC的兼容写法。修改value_pack里的模板模板参数声明,同时同步调整type_map的模板参数结构:

#include <type_traits>

template <typename T>
struct type_identity {
    using type = T;
};

template <typename ...types>
struct type_pack {};

template <auto ...values>
struct value_pack {
  // 修改模板模板参数为显式类型+值的形式
  template <template <typename T, T> typename meta_transformer>
  using transform_to_type = type_pack<typename meta_transformer<decltype(values), values>::type...>;
};

// 同步修改type_map的模板参数声明
template <typename T, T i>
struct type_map;

template <>
struct type_map<int, 1> : type_identity<int> {};

template <>
struct type_map<double, 2.0> : type_identity<double> {};

template <>
struct type_map<char, 'c'> : type_identity<char> {};

int main() {
    using mapped_types = value_pack<1, 2.0, 'c'>::transform_to_type<type_map>;
    static_assert(std::is_same_v<mapped_types, type_pack<int, double, char>>, "Error!");
    return 0;
}

修改后,main函数里的代码不需要改动,就能在MSVC 19.29和GCC下同时编译通过。

3. 其他兼容MSVC的实现方式

如果不想修改原type_map的结构,可以新增一个中间辅助模板做适配,保持原type_map的接口不变:

#include <type_traits>

template <typename T>
struct type_identity {
    using type = T;
};

template <typename ...types>
struct type_pack {};

// 保留原type_map的声明和特化
template <auto i>
struct type_map;

template <>
struct type_map<1> : type_identity<int> {};

template <>
struct type_map<2.0> : type_identity<double> {};

template <>
struct type_map<'c'> : type_identity<char> {};

// 新增辅助模板,适配MSVC的模板模板参数要求
template <typename T, T val>
struct adapt_type_map : type_map<val> {};

template <auto ...values>
struct value_pack {
  template <template <auto> typename meta_transformer>
  // 通过辅助模板间接调用原meta_transformer
  using transform_to_type = type_pack<typename adapt_type_map<decltype(values), values>::type...>;
};

int main() {
    using mapped_types = value_pack<1, 2.0, 'c'>::transform_to_type<type_map>;
    static_assert(std::is_same_v<mapped_types, type_pack<int, double, char>>, "Error!");
    return 0;
}

这种方式不需要改动原type_map的特化代码,仅通过中间层完成适配,同样能在两个编译器下兼容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 17:43:18