通过类型别名从构造函数转发模板推论

时间:2018-09-11 01:09:46

标签: c++ syntax sfinae

这个有关C ++语法以及特定功能的问题。

我有一个Employee类,带有2个数组成员函数。一个数组存储Employee拥有的所有空闲时间,另一个数组存储Employee涵盖的班次。我想定义一个帮助程序类,使我可以在两个数组中的任何一个上使用for范围循环(for (:))。例如,如果我这样做:

for (auto& ts : employee_freetime_iterator{ employee })

它将遍历员工的空闲时间。如果我这样做:

for (auto& ts : employee_shift_iterator{ employee })

它将遍历所有班次。我有一个定义如下的类:

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);
};

在声明中,T将是Employeeconst Employee,而第3个模板参数是SFINAE来强制执行此操作。现在,如果我使用此类,那么我将不得不复制并粘贴两次,一次用于employee_freetime_iterator,一次用于employee_shift_iterator。为了减少代码冗余,我选择这样做:

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);
};

现在,我可以使用ScheduleType选择不同的帮助器函数,这些函数可以使我在Employee类上进行迭代。我想做的是像这样创建2个不同类型的别名(每个ScheduleType一个):

template <typename T>
using employee_freetime_iterator = employee_iterator<T, FREE>;

template <typename T, typename SFINAE>
using employee_shift_iterator = employee_iterator<T, SHIFT>;

但是如何转发构造函数参数,以便自动推断T模板参数?只是按原样编译会给我这个错误:

src/main.cpp:47:40: error: missing template arguments before ‘{’ token
  auto test = employee_freetime_iterator{ em };

em是我之前在代码中创建的一名员工。我已经重构了代码,并删除了不必要的部分并将其粘贴到下面。

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>;

编辑:我知道我写的代码很长且令人困惑,所以我创建了一些很短的东西来显示我的问题。我该如何使用它?

#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};
}

1 个答案:

答案 0 :(得分:1)

如果需要模板参数推导,则需要引入函数:

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};
}

不幸的是,模板类型别名不允许类模板参数的推导。如果要放弃显式模板参数,则必须使用功能模板。