C++17及更早版本下,如何实现基于值的可变参数模板(替代类型参数)?
C++17及更早版本下,如何实现基于值的可变参数模板(替代类型参数)?
嘿,这个需求太贴合实际了!其实C++11开始就支持非类型可变参数模板,正好能替代你现在用的宏,完全符合你的要求,我给你一步步拆解实现:
首先,核心思路是用C++17的auto...作为非类型可变模板参数——这正是你要的“基于值的可变参数”,不用依赖类型参数,所有参数都是编译期常量值。
基础实现:直接匹配你的Sample类需求
我们可以直接写出符合你例子的模板类:
#include <string> template <class T, auto... Args> class Sample { public: T val() const { // 直接把模板参数传递给T的构造函数 return T(Args...); } };
现在来验证你的例子:
Sample<std::string> a;:调用std::string()的默认构造,返回空字符串,完全符合你的预期;Sample<std::string, 4, 'a'> c;:调用std::string(4, 'a'),生成"aaaa",完美匹配;
处理字符串字面量的小坑
这里有个需要注意的细节:你例子里的Sample<std::string, "foo"> b;直接写会报错——因为C17还不允许直接把字符串字面量作为非类型模板参数(这个特性要到C20才支持)。不过我们可以用一个简单的绕路方案:把字符串字面量定义成一个静态的constexpr字符数组:
// 定义一个具有外部链接的constexpr字符串数组 constexpr const char foo_str[] = "foo"; // 现在就可以正常使用了 Sample<std::string, foo_str> b;
这样b.val()就会返回"foo",完全满足你的需求。
进阶扩展:更灵活的参数处理
如果你的需求不仅仅是把参数传给构造函数,而是要对参数做自定义处理(比如拼接不同类型的参数成字符串),C++17的折叠表达式可以帮你轻松实现:
#include <string> #include <type_traits> template <class T, auto... Args> class Sample { private: // 辅助函数:把单个模板参数转换成字符串 template <auto Arg> std::string to_string() const { if constexpr (std::is_same_v<decltype(Arg), char>) { return std::string(1, Arg); } else if constexpr (std::is_integral_v<decltype(Arg)>) { return std::to_string(Arg); } else if constexpr (std::is_same_v<decltype(Arg), const char*>) { return std::string(Arg); } // 可以根据需要扩展更多类型的转换 } public: T val() const { if constexpr (std::is_same_v<T, std::string>) { std::string result; // C++17折叠表达式:依次拼接所有参数的字符串形式 (result += to_string<Args>(), ...); return result; } else { // 非字符串类型还是直接调用构造函数 return T(Args...); } } };
比如Sample<std::string, 42, '!', "bar"> f;的val()会返回"42!bar",非常灵活。
符合你的所有要求吗?
我们来核对一下:
- 完全没有用宏:纯模板实现,彻底摆脱预处理;
- 仅用C17特性:
auto非类型参数、折叠表达式都是C17标准,没有用到更高版本的特性; - 不需要构造函数传参:所有参数都是模板实例化时指定的,完全符合你“不用构造函数传递”的要求。
备注:内容来源于stack exchange,提问作者Thierry Lelegard
相关产品推荐
相关产品推荐

