如何为指向支持operator<类型的指针模板类条件实现insert_sorted函数
PtrVector模板类条件化实现insert_sorted成员函数(声明与实现分离)
问题描述
我有一个容器模板类PtrVector,内部聚合了类型为T的std::vector,其中T实际是指向类型S的指针。我希望创建成员函数insert_sorted(),仅当T指向的类型S支持operator<()时才会被实例化。
我对模板仅了解基础内容,类型特性领域完全陌生。查阅了诸多类似问题后仍搞不清正确语法,似乎需要用到std::enable_if,但不知具体如何操作。理想情况下希望将函数的声明与实现分离,使用环境为MSVC 2022(基于C++17标准)。
现有代码(可编译,除insert_sorted外正常工作)
#include <vector> #include <algorithm> #include <type_traits> #include <utility> #include <assert.h> // 禁用警告 C4251 和 C4275 #pragma warning(disable:4251 4275) namespace supports { namespace details { struct return_t {}; } template<typename T> details::return_t operator<(T const&, T const&); template<typename T> struct less_than : std::integral_constant<bool, !std::is_same<decltype(std::declval<std::remove_pointer<T> const&>() < std::declval<std::remove_pointer<T> const&>()), details::return_t>::value> {}; template<typename T> struct greater_than : std::integral_constant<bool, !std::is_same<decltype(std::declval<T const&>() > std::declval<T const&>()), details::return_t>::value> {}; template<typename T> struct equal : std::integral_constant<bool, !std::is_same<decltype(std::declval<T const&>() == std::declval<T const&>()), details::return_t>::value> {}; template<typename T> struct not_equal : std::integral_constant<bool, !std::is_same<decltype(std::declval<T const&>() != std::declval<T const&>()), details::return_t>::value> {}; } // PtrVector template<typename T> class PtrVector { public: typedef typename std::vector<T>::value_type value_type; typedef typename std::vector<T>::size_type size_type; typedef typename std::vector<T>::reference reference; typedef typename std::vector<T>::const_reference const_reference; typedef typename std::vector<T>::pointer pointer; typedef typename std::vector<T>::const_pointer const_pointer; typedef typename std::vector<T>::iterator iterator; typedef typename std::vector<T>::const_iterator const_iterator; typedef typename std::vector<T>::reverse_iterator reverse_iterator; typedef typename std::vector<T>::const_reverse_iterator const_reverse_iterator; PtrVector(bool owner = true, size_type reserve = 0); iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; // 最初尝试的声明(存在问题) bool insert_sorted(const value_type& val, typename std::enable_if_t<supports::less_than<T>::value, int> = 0); protected: std::vector<T> m_container; }; // 最初尝试的实现 template<typename T> bool PtrVector<T>::insert_sorted(const value_type& val, typename std::enable_if_t<supports::less_than<T>::value, int>) { auto it = std::lower_bound(begin(), end(), val, [](const value_type& a, const value_type& b)->bool { return *a < *b; }); if (it == end() || *val < *(*it)) { m_container.insert(it, val); return true; } return false; }
已实现但无法分离的写法
以下写法可以正常工作,但函数实现必须放在类声明内部:
template <typename = std::enable_if_t<supports::less_than<T>::value, int>> bool insert_sorted(const value_type& val) { auto it = std::lower_bound(begin(), end(), val, [](const value_type& a, const value_type& b)->bool { return *a < *b; }); if (it == end() || *val < *(*it)) { m_container.insert(it, val); return true; } return false; }
错误的分离写法(声明不匹配)
我尝试了如下分离写法,但编译器提示声明与实现不匹配:
类内声明
template<typename = std::enable_if_t<supports::less_than<T>::value, int>> bool insert_sorted(const value_type& val);
类外实现
template<typename T, typename = std::enable_if_t<supports::less_than<T>::value, int>> bool PtrVector<T>::insert_sorted(const value_type& val) { auto it = std::lower_bound(begin(), end(), val, [](const value_type& a, const value_type& b)->bool { return *a < *b; }); if (it == end() || *val < *(*it)) { m_container.insert(it, val); return true; } return false; }
正确的分离写法
问题出在成员模板的定义语法上:类内声明的是成员自身的模板参数,而非类的模板参数。定义时需要先声明类的模板参数,再声明成员的模板参数,具体如下:
类内声明
template<typename = std::enable_if_t<supports::less_than<T>::value, int>> bool insert_sorted(const value_type& val);
类外实现
template<typename T> template<typename Dummy> // 参数名可任意,仅用于匹配声明的模板参数 bool PtrVector<T>::insert_sorted(const value_type& val) { auto it = std::lower_bound(begin(), end(), val, [](const value_type& a, const value_type& b)->bool { return *a < *b; }); if (it == end() || *val < *(*it)) { m_container.insert(it, val); return true; } return false; }
解释
- 类内的
insert_sorted是一个成员模板,带有一个默认模板参数(利用std::enable_if_t实现SFINAE,仅当S支持operator<()时才会实例化该函数)。 - 类外定义时,必须先写类的模板参数
template<typename T>,再写成员的模板参数template<typename Dummy>,这样才能和类内的声明匹配。 - 成员模板的参数名(这里的
Dummy)不影响功能,因为调用时会使用默认参数,无需显式指定。
额外优化:简化类型检查(可选)
C++17提供了std::is_invocable_r,可以替代手写的supports::less_than,代码更简洁且可读性更高:
// 替换原有的supports::less_than template<typename T> constexpr bool has_less_than_v = std::is_invocable_r_v<bool, decltype(&operator<), const std::remove_pointer_t<T>&, const std::remove_pointer_t<T>&>;
之后在enable_if中直接使用has_less_than_v<T>即可。
内容的提问来源于stack exchange,提问作者marcbf
相关产品推荐
相关产品推荐

