如何实现支持任意类型容器的compile-time type list映射?
Absolutely, this generalization is totally feasible! The key issue with your previous attempts is that you're trying to work directly with the instantiated container type (like TestVariant, which is std::variant<int, char, std::array<float, 0>>) but haven't set up a way to extract the underlying container template and its type parameters automatically. Let's fix this step by step.
Correct Implementation
First, we need a helper trait that can decompose any instantiated container into its template and the list of type parameters. Here's the robust solution:
#include <type_traits> namespace impl_ { // Primary template: fallback for non-template-instantiation types template <typename Container, template <typename> typename Transform> struct MapContainer; // Specialization for variadic type-only containers (variant, tuple, etc.) template <template <typename...> typename ContainerTemplate, typename... Ts, template <typename> typename Transform> struct MapContainer<ContainerTemplate<Ts...>, Transform> { // Apply transform to each type and repack into the original container template using Result = ContainerTemplate<Transform<Ts>...>; }; // Optional: Specialization for std::array (handles non-type size parameter) template <typename T, std::size_t N, template <typename> typename Transform> struct MapContainer<std::array<T, N>, Transform> { using Result = std::array<Transform<T>, N>; }; } // User-facing alias for simplicity template <typename Container, template <typename> typename Transform> using MapContainer = typename impl_::MapContainer<Container, Transform>::Result;
How This Works
- The primary
MapContainertemplate acts as a fallback for types that aren't template instantiations (we don't need to handle these for your use case). - The core specialization matches any type that's an instance of a variadic type-only template (like
std::variantorstd::tuple). It extracts:ContainerTemplate: The original container template (e.g.,std::variant)Ts...: The type parameters inside the containerTransform: Your type-level transformation function
- We then create the result by applying
Transformto eachTsand repacking them into the original container template. - The optional
std::arrayspecialization handles containers with non-type parameters (the sizeNin this case).
Test Cases
Let's verify this works with your original variant, a tuple, and an array:
#include <variant> #include <tuple> #include <array> #include <string> // Your original transformation template <typename Arg> using TypeTransform = std::array<Arg, 0>; // Test variant transformation using TestVariant = std::variant<int, char, std::array<float, 0>>; static_assert( std::is_same_v<MapContainer<TestVariant, TypeTransform>, std::variant<std::array<int, 0>, std::array<char, 0>, std::array<std::array<float, 0>, 0>> >, "Variant mapping failed!" ); // Test tuple transformation using TestTuple = std::tuple<int, double, std::string>; static_assert( std::is_same_v<MapContainer<TestTuple, TypeTransform>, std::tuple<std::array<int, 0>, std::array<double, 0>, std::array<std::string, 0>> >, "Tuple mapping failed!" ); // Test array transformation using TestArray = std::array<int, 5>; static_assert( std::is_same_v<MapContainer<TestArray, TypeTransform>, std::array<std::array<int, 0>, 5> >, "Array mapping failed!" );
Why Your Previous Attempts Failed
The "simple" version:
template <template <typename...> class C, template <typename> typename F, typename... Ts> using MapVariant = C<F<Ts>...>;This requires you to pass the container template (e.g.,
std::variant) and type list (e.g.,int, char) separately, but you want to pass the already-instantiatedTestVariantdirectly. It doesn't decompose the existing container type automatically.The verbose version:
Your second attempt had mismatched template parameters and couldn't correctly infer the container template from the instantiated type. The compiler error about "template argument for template template parameter must be a class template" came from trying to passVariant<Ts...>(an instantiated type) where a template template parameter (likestd::variant) was expected.
内容的提问来源于stack exchange,提问作者Pavel Kirienko

