C++模板模板参数能否接受带非类型参数的模板?如何兼容这类容器?
解决C++ Fmap函数适配含非类型模板参数容器的问题
问题核心
你的Fmap函数使用仅接受类型模板参数的模板模板参数(template <typename...> typename Container),而自定义容器MyContainer包含非类型模板参数(int Foo),导致编译器因模板参数不匹配报错。
问题解答
1. 是否可以抽象所有模板参数(包括非类型参数)?
- C++20及以后:可以
C++20放宽了模板模板参数的限制,允许其参数包混合类型、非类型甚至模板参数。你可以通过更灵活的模板模板参数结合元编程,实现对含非类型参数容器的适配。 - C++20之前:不行
旧标准中模板模板参数仅能匹配全类型参数的模板,无法兼容带非类型参数的容器。
2. 无需特化自定义容器的更优代码结构
以下是几种无需特化MyContainer及同类容器的解决方案:
方案一:C++20推荐方案(使用std::ranges)
利用C++20的范围库,通过transform视图映射元素,再用to适配器直接生成目标容器,无需手动处理模板参数:
#include <ranges> template <typename F, std::ranges::input_range Container> auto Fmap(F&& f, const Container& input) { return input | std::views::transform(std::forward<F>(f)) | std::ranges::to<std::decay_t<Container>>(); }
说明:
- 自动适配所有满足
input_range概念的容器(包括STL容器和自定义容器)。 - 若自定义容器无范围构造函数,只需添加一个简单的构造函数即可,无需修改
Fmap代码。
方案二:C++20兼容方案(元编程替换模板参数)
通过元编程提取容器的模板参数,替换元素类型后构造新容器:
#include <type_traits> #include <tuple> #include <utility> // 元函数:替换参数列表中的第一个类型参数 template <typename NewT, typename... Args> struct replace_first_arg; template <typename NewT, typename First, typename... Rest> struct replace_first_arg<NewT, First, Rest...> { using type = std::tuple<NewT, Rest...>; }; template <typename NewT> struct replace_first_arg<NewT> { using type = std::tuple<>; }; // 元函数:将tuple参数转换为容器类型 template <template <typename...> typename Container, typename Tuple> struct tuple_to_container; template <template <typename...> typename Container, typename... Ts> struct tuple_to_container<Container, std::tuple<Ts...>> { using type = Container<Ts...>; }; template <typename F, template <typename...> typename Container, typename... Args> auto Fmap(F&& f, const Container<Args...>& input) { using T = typename Container<Args...>::value_type; using U = std::invoke_result_t<F&, const T&>; // 替换第一个模板参数为映射后的类型 using NewArgs = typename replace_first_arg<U, Args...>::type; using ResultContainer = typename tuple_to_container<Container, NewArgs>::type; ResultContainer result; for (const auto& elem : input) { result.insert(result.end(), f(elem)); } return result; }
说明:
- 适用于C20及以后,支持带非类型参数的容器(C20允许模板模板参数匹配混合类型/非类型参数的容器)。
- 假设容器的第一个模板参数为元素类型(大多数序列容器符合此规则)。
方案三:C++11/14兼容方案(利用rebind机制)
若容器支持rebind嵌套模板(STL容器通过分配器间接支持),可直接复用该机制构造目标容器:
#include <type_traits> #include <utility> template <typename F, typename Container> auto Fmap(F&& f, const Container& input) -> typename Container::template rebind< typename std::invoke_result<F&, const typename Container::value_type&>::type>::other { using T = typename Container::value_type; using U = typename std::invoke_result<F&, const T&>::type; using ResultContainer = typename Container::template rebind<U>::other; ResultContainer result; for (const auto& elem : input) { result.insert(result.end(), f(elem)); } return result; }
说明:
- 需自定义容器
MyContainer添加rebind成员(无需特化Fmap):template <typename Key, int Foo = 256> class MyContainer { public: using value_type = Key; template <typename U> struct rebind { using other = MyContainer<U, Foo>; }; // ...其他成员 };
内容的提问来源于stack exchange,提问作者jacobsa
相关产品推荐
相关产品推荐

