[英]Why is nullptr a part of the core language, but nullptr_t is a part of STL?
[英]Template function for STL iterators, pointers and std::nullptr_t
我正在尝试围绕基于 c 的 API function 编写一个包装器。
用户应该能够将 RandomAccessIterators、指针或 nullptr 作为参数传递给包装器 function。
#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.) 将功能拆分为两个功能。 这里的问题是它会导致不必要的复制粘贴代码。 此外,我想在原始 function 中添加几个重载,这将导致最后的函数数量增加两倍(例如,对于 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
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.