C++17中如何让Optional类模板通过构造函数推导第二个模板参数?
解决
Optional<Error> opt2{4};的编译问题 嘿,这个问题其实是C++17类模板参数推导(CTAD)的一个常见限制:默认情况下,类模板要么要求你显式指定所有模板参数,要么让编译器推导全部参数,没法只指定一部分参数再推导剩下的。不过我们可以通过添加类模板推导指引来实现你想要的效果,让编译器从构造函数参数里推导出第二个模板参数T,同时保留第一个参数Error的显式指定。
修改后的完整代码
#include <variant> #include <type_traits> // 用于std::decay_t处理值类型转换 template <typename Error, typename T> class Optional { public: constexpr Optional(T&& value) : m_variant(std::forward<T>(value)) {} private: std::variant<Error, T> m_variant = Error{}; }; // 关键:添加这个推导指引,告诉编译器如何推导模板参数 template <typename E, typename T> Optional(T&&) -> Optional<E, std::decay_t<T>>; template <typename E, typename T> Optional<E, T> MakeOptional(T&& val) { return Optional<E, T>{val}; } enum class Error { ERROR, OTHER_ERROR, };
为什么这样能工作?
当你写Optional<Error> opt2{4};时:
- 你已经显式指定了第一个模板参数
E为Error - 编译器会匹配我们添加的推导指引,从构造函数参数
4推导出T的类型(这里是int&&) std::decay_t<T>会把引用/右值引用转换为对应的原始值类型(比如把int&&转成int),确保Optional的模板参数T是值类型,和你原来的设计一致- 最终编译器会实例化
Optional<Error, int>,和你显式写Optional<Error, int> opt1{4};的效果完全相同
额外细节
- 原来的代码
Optional<Error, int> opt1{4};和auto opt3 = MakeOptional<Error>(7);仍然可以正常编译,不会受到任何影响 - 如果你的场景只需要处理右值参数,也可以去掉
std::decay_t,直接写推导指引:
但推荐保留template <typename E, typename T> Optional(T&&) -> Optional<E, T>;std::decay_t,这样代码也能正确处理左值参数,比如:int x = 5; Optional<Error> opt2{x}; // 会推导为Optional<Error, int>
C++20+的简化写法
如果你使用的是C++20或更高版本,还有更简洁的方式——直接用显式部分类模板参数推导,不需要添加推导指引:
Optional<Error, auto> opt2{4};
编译器会自动把auto推导为int,不过这只适用于C20及以上版本,而你的问题基于C17,所以推导指引的方法是更合适的选择。
内容的提问来源于stack exchange,提问作者pettitpeon
相关产品推荐
相关产品推荐

