2015-06-21 1 views
4

У меня есть перегрузки шаблонов для operator>>(), где мне нужно различать контейнеры, которые могут быть изменены, например vector, и контейнеры, которые не могут, например, array. В настоящее время я просто использую признак allocator_type (см. Код ниже) - и он отлично работает, но задавался вопросом, есть ли более явный способ тестирования этого.Есть ли лучший способ отличить изменяемые размеры контейнеров, чем наличие allocator_type?

template <class T> 
struct is_resizable { 
    typedef uint8_t yes; 
    typedef uint16_t no; 

    template <class U> 
    static yes test(class U::allocator_type *); 

    template <class U> 
    static no test(...); 

    static const bool value = sizeof test<T>(0) == sizeof yes; 
}; 

template <typename C> 
typename boost::enable_if_c< 
    boost::spirit::traits::is_container<C>::value && is_resizable<C>::value, 
    istream & 
>::type 
operator>>(istream &ibs, C &c) 
{ 
    c.resize(ibs.repeat() == 0 ? c.size() : ibs.repeat()); 
    for (typename C::iterator it = c.begin(); it != c.end(); ++it) 
    { 
     C::value_type v; 
     ibs >> v; 
     *it = v; 
    } 
    return ibs; 
} 

template <typename C> 
typename boost::enable_if_c< 
    boost::spirit::traits::is_container<C>::value && !is_resizable<C>::value, 
    istream & 
>::type 
operator>>(istream &ibs, C &c) 
{ 
    for (typename C::iterator it = c.begin(); it != c.end(); ++it) 
     ibs >> *it; 
    return ibs; 
} 

ответ

0

Благодаря помощи @ Jarod42 на отдельный вопрос , У меня есть решение, которое работает с C++ 98, C++ 03 и C++ 11; g ++ и VS2015. Кроме того, для проблемы ребенка, std::vector<bool>.

#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)    \ 
    template <typename U>             \ 
    class traitsName               \ 
    {                  \ 
    private:                 \ 
     typedef boost::uint8_t yes; typedef boost::uint16_t no;    \ 
     template<typename T, T> struct helper;        \ 
     template<typename T> static yes check(helper<signature, &funcName>*);\ 
     template<typename T> static no check(...);       \ 
    public:                 \ 
     static const bool value = sizeof check<U>(0) == sizeof(yes);   \ 
    } 

DEFINE_HAS_SIGNATURE(has_resize_1, T::resize, void (T::*)(typename T::size_type)); 
DEFINE_HAS_SIGNATURE(has_resize_2, T::resize, void (T::*)(typename T::size_type, \ 
    typename T::value_type)); 

Это как он используется внизу. Обратите внимание, что отмечены как сигнатуры-функции , так и has_resize_2 для resize(). Это связано с тем, что до C++ 11 resize() имел единственную подпись с двумя параметрами, последний с значением по умолчанию; с C++ 11, он имеет две сигнатуры: одну с одним параметром, а другую с двумя параметрами. Более того, VS2015, по-видимому, имеет три подписи - все вышеперечисленное. Решение состоит в том, чтобы просто проверять наличие обеих подписей.

Возможно, есть способ объединить две проверки в одну черту типа, например has_resize<C>::value. Скажите мне, знаете ли вы.

template <typename T> 
typename boost::enable_if_c< 
    !boost::spirit::traits::is_container<T>::value, 
    xstream &>::type 
    operator>>(xstream &ibs, T &b) 
{ 
    return ibs; 
} 

template <typename C> 
typename boost::enable_if_c< 
    boost::spirit::traits::is_container<C>::value && 
    (has_resize_1<C>::value || has_resize_2<C>::value), 
    xstream & 
>::type 
operator>>(xstream &ibs, C &c) 
{ 
    typename C::value_type v; 
    ibs >> v; 
    return ibs; 
} 

template <typename C> 
typename boost::enable_if_c< 
    boost::spirit::traits::is_container<C>::value && 
    !(has_resize_1<C>::value || has_resize_2<C>::value), 
    xstream & 
>::type 
operator>>(xstream &ibs, C &c) 
{ 
    typename C::value_type v; 
    ibs >> v; 
    return ibs; 
} 
1

Да. Вам нужно определить/использовать пользовательский признак (например, boost::spirit::traits).

Наличие или отсутствие распределителя на самом деле не говорит вам, является ли контейнер фиксированным. Нестандартные контейнеры могут не иметь allocator_type соответствующий тип вообще, в то время как все еще позволяя resize(...)

В самом деле, так как вы эффективны заявляющей Концепции что позволяет

C::resize(size_t) 

вы могли бы просто использовать выражение SFINAE для этого

4

Если вы хотите протестировать контейнер, то есть resize -able, вам стоит просто проверить, есть ли у него функция resize(). В C++ 03, которые будут выглядеть следующим образом:

template <typename T> 
class has_resize 
{ 
private: 
    typedef char yes; 
    struct no { 
     char _[2]; 
    }; 

    template <typename U, U> 
    class check 
    { }; 

    template <typename C> 
    static yes test(check<void (C::*)(size_t), &C::resize>*); 

    template <typename C> 
    static no test(...); 

public: 
    static const bool value = (sizeof(test<T>(0)) == sizeof(yes)); 
}; 
+1

Это не сработает; нет никакой гарантии, что подпись 'resize' на самом деле' void (size_t) '. – Mehrdad

+0

@Mehrdad, это, вероятно, достаточно близко для моих нужд. – plong

+0

@ user4438540: Я сомневаюсь, что этого достаточно ... по крайней мере, вы хотите проверить на 'void (size_t, T)' тоже. – Mehrdad

1

Современный C++ имеет очень лаконичный способ:

template <typename T, typename = int> 
struct resizable : std::false_type {}; 

template <typename T> 
struct resizable <T, decltype((void) std::declval<T>().resize(1), 0)> : std::true_type {}; 

Demo

Теперь, если вам не нужно неоднозначности между функциями-членами и переменные-члены с именем resize, вы можете написать выше decltype следующим образом:

decltype((void) &T::resize, 0) 

отмечает, что приведение к void делаются для обработки случаев, когда тип перегружает оператор запятой и обобщающие терпит неудачу (так это просто лучше перестраховаться, чем потом сожалеть политики)

+0

VS2015 жалуется на ваше решение с «ошибкой C2228: слева от« .resize »должен иметь класс/struct/union» и «note: type is» add_rvalue_reference <_Ty> :: type '". – plong

+0

@ user4438540 [1] (https://connect.microsoft.com/VisualStudio/feedback/details/779916/decltype-and-declval-dont-work-as-expected) [2] (https://connect.microsoft .com/VisualStudio/feedback/details/797682/c-decltype-of-class-member-access-incompletely-реализован) –

+0

Microsoft говорит во второй ссылке, что эта проблема исправлена: исправление будет включено в будущее релиз Visual C++ ». VS2015 должен быть «будущим выпуском», потому что эта ошибка не появляется, и обходной путь не требуется. Поэтому я думаю, что проблема C2228, которую я вижу, отличается. – plong

Смежные вопросы