我目前正在编写一个类,它有一些花哨的模板来定义它的一些成员,但其中一些模板完全相同。请考虑以下代码(这些都是类成员):
template <typename T, typename = enable_if_t<is_convertible<int, T>::value>>
T get() const { return convert_self_to_int(); }
template <typename T, typename = enable_if_t<is_constructible<T, string>::value>, typename = void>
T get() const { return convert_self_to_string(); }
template <typename T, typename = enable_if_t<is_convertible<int, T>::value>>
operator T() const { return get<T>(); }
template <typename T, typename = enable_if_t<is_constructible<T, string>::value>, typename = void>
operator T() const { return get<T>(); }
正如您所看到的,我有一个名为get
的模板化成员函数,该函数使用较长且难以阅读的模板代码。
这部分对于这个问题并不重要,但这里是对所有这些花哨模板的简要说明:
get
是一个函数, 可以使用以下两种格式之一返回数据:如果模板参数T
是 可以转换int
的类型,然后是积分 返回数据的表示(从而触发转换 到我们知道可能的请求类型。如果T
是某种东西 可以从string
构建,然后string
返回数据的表示(再次,触发数据 从T
构建string
。任何其他没有的类型 落入这些类别只会导致编译时错误, 这正是这段代码的目的。
此类还定义了简单的转换运算符,这些运算符以get
。
既然这些运营商使用完全相同的模板作为get
的相应定义,我能以某种方式避免重复所有讨厌的模板代码吗?我可以重用一行模板代码来定义多个东西,使代码更具可读性吗?
答案 0 :(得分:2)
您可以使用SFINAE将operator T()
转发至get<T>
。这样,您只需要一个operator T()
:
template <class T, class = decltype(std::declval<ClassName>().get<T>())>
operator T() const {
return get<T>();
}
此外,对于多个SFINAE,您可以更改typename=void
,而不是不断添加其他enable_if_t
,以便为您提供默认int
:
template <class T, std::enable_if_t<std::is_convertible<int, T>::value, int> = 0>
T get() const { return convert_self_to_int(); }
template <class T, std::enable_if_t<std::is_convertible<std::string, T>::value, int> = 0>
T get() const { return convert_self_to_string(); }
现在,这不会在clang中起作用,所以我只是建议翻转顺序。让operator T()
成为SFINAE-d:
template <class T, std::enable_if_t<std::is_convertible<int, T>::value, int> = 0>
operator T() const { return convert_self_to_int(); }
template <class T, std::enable_if_t<std::is_convertible<std::string, T>::value, int> = 0>
operator T() const { return convert_self_to_string(); }
只有get
前进:
template <class T> T get() const { return operator T(); }
这里的优点是我们不会复制任何内容,std::is_convertible<>
类型特征将正常工作 - 因为operator T()
是SFINAE-d。对get<T>()
的测试将失败,但这似乎不是通常可测试的东西。