Упрощение кода для отправки разных типов в разные функции на C

#c #templates #c 14

#c #шаблоны #c 14

Вопрос:

Я хочу написать функцию, которая выводит значение по-разному для типов итераторов и других типов. После проведения некоторых исследований, вот что я придумал:

 #include <iostream>
#include <iterator>
#include <experimental/type_traits>

namespace details
{
    template <class T, class = void>
    struct is_iterator : std::false_type
    {
    };

    template <class T>
    struct is_iterator<T, std::experimental::void_t<typename std::iterator_traits<T>::value_type>> : std::true_type
    {
    };

    template <class T>
    constexpr auto is_iterator_v = is_iterator<T>::value;
}

template <class T>
std::enable_if_t<!details::is_iterator_v<T>> print(const T amp;value)
{
    std::cout << value;
}

template <class T>
std::enable_if_t<details::is_iterator_v<T>> print(const T amp;value)
{
    std::cout << "(IteratorTo ";
    print(*value);
    std::cout << ')';
}
  

Но я думаю, что мой код слишком длинный и немного сложный. Возможно ли сделать мой код короче и чище?

Ответ №1:

SFINAE твой друг здесь:

 template<class T>
constexpr std::true_type is_iterator(typename std::iterator_traits<T>::value_type*, int) {
    return {};
}

template<class T>
constexpr std::false_type is_iterator(void*, long) {
    return {};
}

template<class T>
constexpr auto is_iterator_v = is_iterator<T>(nullptr, 0);
  

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

 template<class T>
void print(const T amp;);

namespace details {
    template<class T>
    void print(const T amp;value, std::false_type) {
        std::cout << value;
    }

    template<class T>
    void print(const T amp;value, std::true_type) {
        std::cout << "(IteratorTo ";
        print(*value);
        std::cout << ')';
    }
}

template<class T>
void print(const T amp;value) {
    details::print(value, details::is_iterator_v<T>);
}
  

Комментарии:

1. @aschepler : Справедливые баллы всем! Бесстыдная лень с моей стороны. >_>

2. @ildjarn — хорошее решение ( 1); никогда не думал, что int / long трюк для решения проблемы неоднозначности, когда T является итератором; но как насчет подписи последнего print() в последнем примере? Не должно быть void print (const T amp;value) без std::true_type ?

3. @max66 : Действительно, ошибка неаккуратного копирования / вставки с моей стороны. Спасибо

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

5. @ildjarn — извините, не видел раньше, но … в print(const Tamp;) , не должно быть details::print(value, details::is_iterator_v<T>); вместо details::print(T, details::is_iterator_v<T>); (я имею в виду: value вместо T )? И еще один вопрос: есть причина, потому что в std::false_type версии is_iterator() тип первого аргумента — void const volatile * а не void const * или просто void * ?

Ответ №2:

Я нахожу ваше решение простым, коротким и чистым.

Я вижу только проблему: вы отметили свой вопрос C 14, но вы предлагаете код, который использует std::experimental::void_t то есть… ну … экспериментальный и никогда не доступный.

Но (если я не ошибаюсь), вы могли бы получить тот же результат и без void_t , используя старый трюк с запятой decltype.

Вы можете написать свою std::true_type версию следующим образом (изменена в попытке исправить проблему, на которую указал Ильджарн (спасибо!)):

 template <typename T>
struct is_iterator<T,
   decltype(std::declval<typename std::iterator_traits<T>::value_type>(),
      void())> : std::true_type
 { };
  

Это решение также должно работать с C 11.

Комментарии:

1. Для этого требуется, чтобы value_type можно было создавать по умолчанию; довольно строгое требование!

2. @ildjarn — D’oh! Вы правы. Слишком строгое требование. Поправьте меня, если я ошибаюсь: std::declval предполагается, что это решит эту проблему, не так ли? (смотрите Мой измененный ответ)