#include #include #include #include #include template struct SequenceTraits { // By ensuring that this is nonsense for GridKind, we know that this // version of the template can never apply, and a specialization // must be used. using Unresolved = typename SequenceKind::UnspecializedTraitError; // must publish // types // the kind itself for convenience // position type // value type // behaviors // get first // get next // get value from position }; // We can define a concept in order to ensure that all SequenceTraits // have an appropriate interface, if we so desire. template concept SequenceAccess = // We can express the existence of published types that are required. requires{ typename T::Position; typename T::Value; typename T::Kind; } && // We can also express the constraints on the methods that must be available. requires(T t, typename T::Position position, typename T::Value value, const typename T::Kind sequence) { { T::getFirst(sequence) } -> std::same_as>; { T::getNext(sequence, position) } -> std::same_as>; { T::getValue(position) } -> std::same_as; }; ///////////////////////////////////////////////////////////////////////////// // We can define behaviors that are independent of the specific structures // in question but that make use of information about them. This is done in // part through specialization of the trait information for the types of interest. ///////////////////////////////////////////////////////////////////////////// // Constraining the function using the concept was actually just a matter of // replacing the `typename` keyword template> bool isIncreasing(const SequenceKind& sequence) { using Position = typename Traits::Position; std::optional previous = std::nullopt; std::optional next = Traits::getFirst(sequence); while (next) { if (previous && Traits::getValue(*next) <= Traits::getValue(*previous)) { return false; } previous = next; next = Traits::getNext(sequence, *next); } return true; } template> void printSequence(const SequenceKind& sequence) { using Position = typename Traits::Position; std::optional next = Traits::getFirst(sequence); while (next) { std::cout << Traits::getValue(*next) << " "; next = Traits::getNext(sequence, *next); } std::cout << "\n"; } ///////////////////////////////////////////////////////////////////////////// // In order to make these work, we need to define the types that we care about // along with extra trait information to glue the trait driven code to the type ///////////////////////////////////////////////////////////////////////////// // NOTE: We can write this in a few ways. If we write it as // // template // struct SequenceTraits> // // Then the specialization will work only for vectors. by writing it in the form // below, it instead works for any templated containers like `vector` or `list` // adhering to the sequence APIs used by the STL (push_back, etc). // template