简体   繁体   English

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

[英]Template function for STL iterators, pointers and std::nullptr_t

I'm trying to write a wrapper around a c-based API function.我正在尝试围绕基于 c 的 API function 编写一个包装器。
The user should be able to pass RandomAccessIterators, pointers or the nullptr as parameters to the wrapper 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;
}

This code doesn't compile because std::iterator_traits<T> isn't specialized for std::nullptr_t .此代码无法编译,因为std::iterator_traits<T>不是专门用于std::nullptr_t

I already came up with two possible solutions:我已经想出了两种可能的解决方案:

1.) Specialize std::iterator_traits<std::nullptr_t> , eg: 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;
    };
}

But as far as I know specializing stl namespace members can quickly lead to undefined behavior.但据我所知,专门 stl 命名空间成员会很快导致未定义的行为。

2.) Split the functionality in two functions. 2.) 将功能拆分为两个功能。 The problem here is that it would lead to unnecessary copy-paste code.这里的问题是它会导致不必要的复制粘贴代码。 Additionally I want to add several overloads to the original function which would lead to two times the number of functions at the end (eg for 4 normal overloads I would require 8 overloads with this approach).此外,我想在原始 function 中添加几个重载,这将导致最后的函数数量增加两倍(例如,对于 4 个正常重载,我需要使用这种方法进行 8 个重载)。

So my question is:所以我的问题是:
Is there any way to solve this problem by not introducing possible undefined behavior and having to create two times the number of overloaded functions?有没有办法通过引入可能的未定义行为并且必须创建两倍数量的重载函数来解决这个问题?
For example tweaking the is_random_access_iterator_v trait to not rely on std::iterator_traits ?例如,调整is_random_access_iterator_v特征使其不依赖于std::iterator_traits

You cannot specialize std::iterator_traits<std::nullptr_t> , but you can create your own 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;
};

and use my_iterator_traits for your needs.并根据您的需要使用my_iterator_traits

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM