Practice a few template techniques beyond everyday generic-function usage:
overload resolution between value/rvalue insertion paths, perfect
forwarding, and taking a callable as a template parameter instead of
std::function.
DataStore holds a collection of DataSet (a name plus a vector<double>)
in a std::vector, chosen specifically so it can be sorted in place — an
associative container such as std::map would need a random-access
iterator it doesn't provide, and would have to be rebuilt with a custom
comparator to change ordering. Iteration is exposed directly through the
underlying vector's iterator (begin()/end()), since a dedicated iterator
type isn't needed here.
addDataset(std::string name, std::vector<double> nums)— the copy-and-move path:nameandnumsare taken by value (so the caller's arguments are copied, or moved if the caller passes rvalues), then both are moved into the storedDataSet.template <typename T> addDataset(T&& dataset)— the perfect-forwarding path: a single template handles an existingDataSetpassed as an lvalue, a const lvalue, or an rvalue/prvalue, forwarding it through topush_backso the right one of copy/move is chosen automatically for each case.
sort(func, order) takes the comparison metric as a template parameter
(T func) rather than as a std::function, so the call is resolved at
compile time and avoids the virtual-call/vtable indirection that
std::function's type erasure would otherwise add on every comparison.
func is invoked with a DataSet's _nums begin/end iterators and returns
a double metric to sort by. Because that metric can be expensive to
compute, sort evaluates it once per element up front into a cache
vector and reuses those cached values for every comparison during the sort,
instead of recomputing the metric each time two elements are compared.
main demonstrates both insertion paths and sorts by average value (via
std::accumulate/std::distance over each dataset's numbers), printing the
collection before and after sorting.
include/DataStore.h SortingDirection, DataSet, DataStore<DataSet>
src/main.cpp exercises both insertion paths and sort()
- Compiles cleanly with
-Wall -Wextra -pedantic. DataStorestoresDataSetin astd::vectorand exposesbegin()/end()for iteration.- Two
addDatasetoverloads exist: one following the copy-and-move idiom for an explicit name/values pair, and one perfect-forwarding template for an existingDataSet. sort()takes the comparison metric as a template parameter (notstd::function) and computes/caches it once per element rather than recomputing it on every comparison.
Before sorting:
Tank 1: [12.5, 15.2, 14.8, 13.1]
Tank 2: [22, 19.5, 21.3, 20.7]
Tank 3: [8.4, 7.9, 9.1, 8.7]
Tank 4: [31.2, 29.8, 30.5, 32.1]
Tank 5: [17.6, 18.2, 16.9, 17.3]
Tank 6: [12.6, 2.2, 21.3, 11.5]
Tank 7: [51.2, 32.1, 16.5, 22.4]
After sorting:
Tank 3: [8.4, 7.9, 9.1, 8.7]
Tank 6: [12.6, 2.2, 21.3, 11.5]
Tank 1: [12.5, 15.2, 14.8, 13.1]
Tank 5: [17.6, 18.2, 16.9, 17.3]
Tank 2: [22, 19.5, 21.3, 20.7]
Tank 7: [51.2, 32.1, 16.5, 22.4]
Tank 4: [31.2, 29.8, 30.5, 32.1]