专门针对指针值类型使用通用迭代器的 C++ 模板函数?

Specialize C++ template function that uses generic iterators for pointer value type?

我有一个接受通用迭代器作为参数的 C++ 模板函数,如下所示:

(它处理从 firstlast 的所有元素,但不包括 last

void update(const std::uint8_t *const data, const size_t len)
{
    /* ... */
}

template<typename iterator_type>
void update(const iterator_type &first, const iterator_type &last)
{
    typedef typename std::iterator_traits<iterator_type>::value_type value_type;
    for (iterator_type iter = first; iter != last; ++iter)
    {
        update(reinterpret_cast<const std::uint8_t*>(std::addressof(*iter)), sizeof(value_type));
    }
}

到目前为止,这是有效的。我唯一的问题是:如果迭代器的value_type恰好是指针类型,那么这个函数处理的是指针值(内存地址)而不是实际值(对象) 指针所指向的。所以,我想专门处理指针类型。在 C++ 中是否有一种好方法可以为 value_type 是指针类型的迭代器提供此模板函数的特化?

我试图用一个函数和一个 if(is_pointer)... else... 结构来解决这个问题,就像这样:

void update(const iterator_type &first, const iterator_type &last)
{
    typedef typename std::iterator_traits<iterator_type>::value_type value_type;
    if(std::is_pointer<value_type>::value)
    {
        for (iterator_type iter = first; iter != last; ++iter)
        {
            update(reinterpret_cast<const std::uint8_t*>(*iter), sizeof(std::pointer_traits<value_type>::element_type));
        }
    }
    else
    {
        for (iterator_type iter = first; iter != last; ++iter)
        {
            update(reinterpret_cast<const std::uint8_t*>(std::addressof(*iter)), sizeof(value_type));
        }
    }
}

...但是,不幸的是,这不能为非指针类型编译:

error C2039: 'element_type': is not a member of 'std::pointer_traits<value_type>'

编写模板仿函数来更新单个项目并专门化它更容易。然后,让您的更新模板函数调用 class。类似于:

template <typename T>
struct Update {
    void operator () (const T &v) {
        //...
    }
};

template <typename T>
struct Update<T *> {
    void operator () (const T *v) {
        //...
    }
};


template <typename T>
void update (const T &v) {
    Update<T>()(v);
}

然后在循环中,你可以这样称呼它:

update(*iter);

您可以将更新仿函数参数化到更新函数模板中,以便在调用者想要执行与默认更新处理不同的操作时为调用者​​提供灵活性。

template <typename T, typename U = Update<T>>
void update (const T &v, U u = U{}) {
    u(v);
}

然后,之前的调用仍然有效,但您也可以传入不同的可调用对象(如 lambda)。

update(*iter, [](int *){ /* ... */ });

这个问题已经有了很好的答案,但我会在这里抛出类型特征和标签分派使得指针和非指针值类型的迭代器之间的重载变得容易:

template <typename T>
void update_tag_dispatch(T first, T last, std::true_type) {
    std::cout << "iterator value is a pointer!\n";
}

template <typename T>
void update_tag_dispatch(T first, T last, std::false_type) {
    std::cout << "iterator value is not a pointer\n";
}

template <typename T>
void update_tag_dispatch(T first, T last) {
    update_tag_dispatch(first, last, std::is_pointer<std::remove_reference_t<decltype(*first)>>{});
}

你也可以使用它的表亲,std::enable_if_t:

template <typename T, std::enable_if_t<std::is_pointer<std::remove_reference_t<decltype(*std::declval<T>())>>::value>* = nullptr>
void update_enable_t(T first, T last) {
    std::cout << "iterator value is a pointer!\n";
}
template <typename T, std::enable_if_t<!std::is_pointer<std::remove_reference_t<decltype(*std::declval<T>())>>::value>* = nullptr>
void update_enable_t(T first, T last) {
    std::cout << "iterator value is a pointer!\n";
}

演示:https://godbolt.org/z/t7YbcF