Специализировать шаблонную функцию C ++, которая использует итераторы generi c для типа значения указателя? - PullRequest
1 голос
/ 05 января 2020

У меня есть шаблонная функция C ++, которая принимает обобщенные итераторы c в качестве аргументов, как показано ниже:

(она обрабатывает все элементы от first до, но исключая 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>'

Ответы [ 3 ]

1 голос
/ 06 января 2020

Проще написать функтор шаблона для обновления отдельного элемента и специализировать его. Затем, пусть ваша функция шаблона обновления вызывает класс. Что-то вроде:

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);
}

Затем внутри l oop вы называете это следующим образом:

update(*iter);

Вы можете параметризовать функтор обновления в шаблон функции обновления, чтобы предоставьте вызывающей стороне гибкость, если он хочет сделать что-то отличное от обработки обновлений по умолчанию.

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

Тогда предыдущий вызов все еще работает, но вы также можете передать другой вызываемый объект (например, лямбда).

update(*iter, [](int *){ /* ... */ });
0 голосов
/ 08 января 2020

На этот вопрос уже есть хороший ответ, но я добавлю сюда, что черты типа и диспетчеризация тегов облегчают перегрузку между итераторами с типами значений указателя и не указателя:

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

0 голосов
/ 06 января 2020

ИМХО, это плохой способ программирования перегрузить функцию, а затем использовать один из них внутри другого, кроме как в виде самых простых форм аргументов
попробуйте изменить , называемое update, внутри update другой функцией, кроме OF или, если не слишком много, вставьте / поместите его явно в строку.
imho, чтобы получить лучшую читабельность, положите using namespace std; и сотрите все std::, а также приведите в порядок имя шаблона с несколькими значащими заглавными буквами.
тогда может повторно покажи нам ..

...