Reputation: 638
This question about the syntax of C++ and how do do something specific.
I have an Employee
class with 2 member functions that are arrays. One array stores all the free time the Employee
has and the other stores the shifts the Employee
is covering. I would like to define a helper class that lets me use a for range loop (for (:)
) over either of the arrays. For example if I do this:
for (auto& ts : employee_freetime_iterator{ employee })
It will iterate over the free time the employee has. And if I do this:
for (auto& ts : employee_shift_iterator{ employee })
It will iterate over the shifts. I have a class that is defined like this:
template <typename T,
typename = std::enable_if_t<std::is_same_v<Employee, std::remove_cv_t<T>>>
>
struct employee_shift_iterator {
employee_shift_iterator(T& e);
};
In the declaration, T
is either going to be a Employee
or an const Employee
and the 3rd template parameter is a SFINAE to force this fact. Now If I use this class then I will have to copy and paste it twice, one for employee_freetime_iterator
and one for employee_shift_iterator
. To reduce the code redundancy, I choose to do this:
enum ScheduleType {
FREE,
SHIFT,
ST_TOTAL
};
template <typename T, ScheduleType ST,
typename = std::enable_if_t<std::is_same_v<Employee, std::remove_cv_t<T>>>
>
struct employee_iterator {
constexpr static ScheduleType mScheduleType = ST;
employee_iterator(T& e);
};
Now I can use the ScheduleType
to choose different helper functions that let me iterate over the Employee
class. The thing that I am trying to do is to create 2 different type alias' (one for each ScheduleType
) like this:
template <typename T>
using employee_freetime_iterator = employee_iterator<T, FREE>;
template <typename T, typename SFINAE>
using employee_shift_iterator = employee_iterator<T, SHIFT>;
But how can I forward the constructor parameter so that the T
template param will be automatically inferred? Just compiling as is gives me this error:
src/main.cpp:47:40: error: missing template arguments before ‘{’ token
auto test = employee_freetime_iterator{ em };
Where em is an employee I created earlier in my code. I have refactored the code and removed the unnecessary parts of it and pasted it below.
enum ScheduleType {
FREE,
SHIFT,
ST_TOTAL
};
// Forward declaration
template <typename T, ScheduleType ST, typename SFINAE>
class employee_iterator;
struct Employee {
std::vector<TimeSlot> mFreeTime;
std::vector<TimeSlot> mShifts;
using timeslot_iterator = typename std::vector<TimeSlot>::iterator;
using timeslot_const_iterator = typename std::vector<TimeSlot>::const_iterator;
timeslot_const_iterator begin(const std::vector<TimeSlot>& s) const;
timeslot_const_iterator end(const std::vector<TimeSlot>& s) const;
timeslot_iterator begin(std::vector<TimeSlot>& s);
timeslot_iterator end(std::vector<TimeSlot>& s);
};
// Helper class
template <typename T, ScheduleType ST,
typename = std::enable_if_t<std::is_same_v<Employee, std::remove_cv_t<T>>>
>
struct employee_iterator {
using iterator = std::conditional_t<std::is_const_v<T>, Employee::timeslot_const_iterator, Employee::timeslot_iterator>;
constexpr static ScheduleType mScheduleType = ST;
std::add_pointer_t<T> mEmployee;
employee_iterator() = delete;
employee_iterator(T& e);
employee_iterator(T* e);
};
// Helper class c'tors
template <typename T, ScheduleType ST, typename SFINAE>
employee_iterator<T, ST, SFINAE>::employee_iterator(T& e)
: mEmployee{ &e }
{ }
template <typename T, ScheduleType ST, typename SFINAE>
employee_iterator<T, ST, SFINAE>::employee_iterator(T* e)
: mEmployee{ e }
{ }
// begin and end functions for iteration over Employee
template <typename T, ScheduleType ST, typename SFINAE>
typename employee_iterator<T, ST, SFINAE>::iterator begin(employee_iterator<T, ST, SFINAE> it) {
if constexpr (ST == FREE)
return it.mEmployee->begin(it.mEmployee->mFreeTime);
else
return it.mEmployee->begin(it.mEmployee->mShifts);
}
template <typename T, ScheduleType ST, typename SFINAE>
typename employee_iterator<T, ST, SFINAE>::iterator end(employee_iterator<T, ST, SFINAE> it) {
if constexpr (ST == FREE)
return it.mEmployee->end(it.mEmployee->mFreeTime);
else
return it.mEmployee->end(it.mEmployee->mShifts);
}
/// Type alias
template <typename T>
using employee_freetime_iterator = employee_iterator<T, FREE>;
template <typename T>
using employee_shift_iterator = employee_iterator<T, SHIFT>;
EDIT: I know my code I wrote is long and confusing, so I created something really short that shows my issue. How can I get this to work?
#include <utility>
template <typename T1, typename T2>
using my_pair = std::pair<T1, T2>;
int main() {
// I can do this:
// will be inferred as std::pair<double, int>
std::pair test1{ 1.0, 5 };
// However the compiler has issues with this:
my_pair test2{1.0, 3};
}
Upvotes: 1
Views: 196
Reputation: 96845
If you need template argument deduction you need to introduce functions:
namespace detail{
// ... employee_iterator, ScheduleType etc, ...
template <typename T>
using employee_freetime_iterator = employee_iterator<T, FREE>;
template <typename T>
using employee_shift_iterator = employee_iterator<T, SHIFT>;
}
template<class T>
detail::employee_freetime_iterator<T> employee_freetime_iterator(T& e) {
return {e};
}
template<class T>
detail::employee_shift_iterator<T> employee_shift_iterator(T& e) {
return {e};
}
Unfortunately class template argument deduction is not allowed for template type aliases. You will have to go with a function template if you want to forgo explicit template arguments.
Upvotes: 1