如何在保留概念约束的前提下实现State::Machine模板的单Map参数实例化?
State::Machine Instantiation to a Single Map Parameter (With Full Concept Constraints) Absolutely! You can retain all your concept-based type constraints while simplifying State::Machine instantiation down to just passing your map type (or even letting the compiler deduce it automatically) using C++20 features like Class Template Argument Deduction (CTAD) and type extraction from standard container associated types. Here's a step-by-step solution:
1. Refine Concepts for Type Extraction
First, adjust your core concepts to support extracting types directly from your map and transition objects:
- Update your
Mapconcept to require standard container associated types (key_typeandmapped_type), so we can pull out constraint and transition container types straight from the map. - For the
Transitionconcept, ensure we can deduce the input source type and result types from theState::Productreturn value usingstd::invoke_result_tanddecltype.
// Refined core concepts template <typename M> concept Map = requires(M m) { typename M::key_type; typename M::mapped_type; // Add your existing Map concept constraints here }; template <typename T, typename S, typename C, typename R> concept Transition = requires(T t, S s) { { t(s) } -> std::convertible_to<State::Product<S, C, R>>; }; template <typename Trs, typename T> concept RangeOfTransition = requires(Trs trs) { typename Trs::value_type; requires Transition<typename Trs::value_type, T, /* Deduce remaining types */>; // Add your existing Range::Of constraints here };
2. Redesign State::Machine for Single-Parameter Use
Rewrite the Machine template to accept only a map type (plus optional strategy) and extract all required types from the map. We'll use static assertions to enforce all original concept constraints:
namespace State { enum Strategy { Breadth, Depth, Heuristic }; template <Map M, Strategy S = Strategy::Depth> class Machine { public: // Extract core types directly from the map using Constraint = typename M::key_type; using Transitions = typename M::mapped_type; using Transition = typename Transitions::value_type; // Extract source/result types from the transition's State::Product return value using Product = std::invoke_result_t<Transition, /* Deduce source type */>; using Source = decltype(std::declval<Product>().source); using Result = decltype(std::declval<Product>().product); // Enforce all original concept constraints static_assert(Range::Type<Source>, "Source must satisfy Range::Type concept"); static_assert(Comparable<Constraint>, "Constraint must satisfy Comparable concept"); static_assert(Transition<Transition, Source, Constraint, Result>, "Transition must meet Transition concept requirements"); static_assert(Range::Of<Transition, Transitions>, "Transitions must be a valid range of Transition objects"); // Constructor to accept the map instance explicit Machine(M map) : map_(std::move(map)) {} private: M map_; }; // CTAD Deduction Guide: Let the compiler automatically deduce the map type template <Map M> Machine(M) -> Machine<M>; } // namespace State
Key Details:
Constraintcomes from the map'skey_type, andTransitionsfrom itsmapped_type.SourceandResultare pulled directly from theState::Productreturned by your transition—no need to modifyState::Productunless you want to add nested type aliases for cleaner code.- Static assertions ensure none of your original concept safety is lost.
3. Simplified Instantiation
Now you can instantiate State::Machine exactly how you wanted—either explicitly passing the map type, or letting CTAD handle it automatically:
// Your existing transition and map setup auto transition = [](std::string source) { return State::Product<std::string, State, State> { .source = source, .state = State::Start, .product = State::Start, }; }; std::map<State, std::set<decltype(transition)>> map = {{State::Start, {transition}}}; // Option 1: Explicitly pass the map type (matches your desired syntax) State::Machine<decltype(map)> machine(map); // Option 2: Let CTAD deduce the map type automatically (even cleaner!) State::Machine machine(map);
This approach keeps all your type safety from concepts while drastically simplifying the instantiation process. It works seamlessly with C++20-compliant compilers (GCC 10+, Clang 11+, MSVC 2019+).
内容的提问来源于stack exchange,提问作者Robbie

