用于 STL 迭代器、指针和 std::nullptr_t 的模板函数

Template function for STL iterators, pointers and std::nullptr_t

本文关键字:函数 nullptr 用于 迭代器 指针 std STL      更新时间:2023-10-16

我正在尝试围绕基于 c 的 API 函数编写一个包装器。
用户应该能够将 RandomAccessIterator、指针或 nullptr 作为参数传递给包装器函数。

#include <type_traits>
#include <iterator>
template <typename Iter>
constexpr bool is_random_access_iterator_v = std::is_convertible_v<typename std::iterator_traits<Iter>::iterator_category, std::random_access_iterator_tag>;
template <typename Iter>
using iterater_decayed_type_t = std::decay_t<typename std::iterator_traits<Iter>::value_type>;
// convert iterator to pointer
template <typename Iter>
auto get_pointer(Iter it) {
if constexpr (std::is_null_pointer_v<Iter> || std::is_pointer_v<Iter>) {
return it;
} else {
return &*it;
}
}
// InputIter and OutputIter can be arbitrary iterator or pointer. 
// OutputIter can also be a nullptr
template <typename InputIter, typename OutputIter>
OutputIter wrapper(InputIter first, InputIter last, OutputIter d_first) {
static_assert(is_random_access_iterator_v<InputIter>, "InputIter needs to be a RandomAccessIterator");
static_assert(std::is_null_pointer_v<OutputIter> || is_random_access_iterator_v<OutputIter>, "OutputIter needs to be a RandomAccessIterator or nullptr");
static_assert(std::is_null_pointer_v<OutputIter> || std::is_same_v<iterater_decayed_type_t<InputIter>, iterater_decayed_type_t<OutputIter>>, "Iterator value types must be identical or OutputIter is nullptr");
using value_t = iterater_decayed_type_t<InputIter>;
using first_ptr_t = typename std::iterator_traits<InputIter>::pointer;
first_ptr_t ptr_first = get_pointer(first);
using d_first_ptr_t = std::conditional_t<std::is_null_pointer_v<OutputIter>, std::nullptr_t, typename std::iterator_traits<OutputIter>::pointer>;
d_first_ptr_t ptr_d_first = get_pointer(d_first);
// func gets arbitrary pointers (void*)
func(ptr_first, ptr_d_first, last - first);
return d_first;
}

此代码无法编译,因为std::iterator_traits<T>不是专门用于std::nullptr_t的。

我已经提出了两种可能的解决方案:

1.( 专攻std::iterator_traits<std::nullptr_t>,例如:

namespace std {
template <>
struct iterator_traits<std::nullptr_t> {
using difference_type = std::ptrdiff_t;
using value_type = std::nullptr_t;
using pointer = std::nullptr_t;
using reference = std::nullptr_t;
using iterator_category = std::random_access_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
};
}

但据我所知,专门的 stl 命名空间成员很快就会导致未定义的行为。

2.( 将功能拆分为两个功能。这里的问题是它会导致不必要的复制粘贴代码。此外,我想在原始函数中添加几个重载,这将导致最后函数数量的两倍(例如,对于 4 个正常重载,使用这种方法我需要 8 个重载(。

所以我的问题是:
有没有办法通过引入可能的未定义行为并且必须创建两倍数量的重载函数来解决此问题?
例如,调整is_random_access_iterator_v特征以不依赖std::iterator_traits

你不能专化std::iterator_traits<std::nullptr_t>,但你可以创建自己的特征:

template <typename T>
struct my_iterator_traits : iterator_traits<T> {};
template <>
struct my_iterator_traits<std::nullptr_t>
{
using difference_type = std::ptrdiff_t;
using value_type = std::nullptr_t;
using pointer = std::nullptr_t;
using reference = std::nullptr_t;
using iterator_category = std::random_access_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
};

并根据您的需要使用my_iterator_traits