Ошибка is_container в std :: set SFINAE - PullRequest
       26

Ошибка is_container в std :: set SFINAE

2 голосов
/ 11 февраля 2012

Я пытаюсь написать оператор потока для контейнеров std, главным образом для отладки.

У меня есть следующий код:

#include <type_traits>
#include <iostream>
#include <ostream>
#include <iterator>
#include <algorithm>
#include <functional>

#include <vector>
#include <set>
#include <deque>


template<typename Container>
struct is_container
{
    typedef char no;
    typedef long yes;


    template<typename A, A, A>
    struct is_of_type;

    template<typename T>
            static yes& is_cont(
                            is_of_type
                            <
                                    typename T::iterator(T::*)(), 
                                    &T::begin,
                                    &T::end
                            >*);


    template<typename T>
    static no& is_cont(...);        //any other

    template<typename C, bool B>
    struct is_class_is_container
    {
            const static bool value=sizeof( is_cont<C>(nullptr) )==sizeof(yes);
    };

    template<typename C>
    struct is_class_is_container<C, false>
    {
            const static bool value=false;
    };

    const static bool value = is_class_is_container
            <
                    Container, 
                    std::is_class<Container>::value 
            >::value;
};

template<typename T>
    typename std::enable_if
    < is_container<T>::value, std::ostream >::type& 
    operator<<(std::ostream& os, const T& a)
{
    os << '[';
    std::copy(a.begin(), a.end(), std::ostream_iterator<typename T::value_type>(os, ", "));
    os << ']';
    return os;
}

Я знаю, что это далеко отидеально (конструктивные комментарии приветствуются), но проблема, с которой я столкнулся, заключается в том, что он отлично работает для вектора, deque и list, но не подходит для наборов, я не знаю почему, потому что наборы все еще имеют интерфейсы итератора, начинающиеся и заканчивающиеся.

Спасибо.

РЕДАКТИРОВАТЬ: протестировано на g ++ (GCC) 4.6.2 2012012 clang версия 3.0

РЕДАКТИРОВАТЬ2: я вроде как работает с использованием decltype, однако это неоптимально, потому что теперь яне могу утверждать, что он выполняет то, что я ожидаю (возвращаю итератор).

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

Ответы [ 2 ]

4 голосов
/ 11 февраля 2012

Поскольку std::set<T> имеет только один набор неизменяемых итераторов, существует только одна версия begin() и end(), которая объявлена ​​как const. То есть определение std::set<T> выглядит примерно так (при условии, что оно было объявлено в пространстве имен std ранее):

template <typename T>
class std::set
{
public:
    class            iterator;
    typedef iterator const_iterator;
    ...
    const_iterator begin() const;
    const_iterator end() const;
    ...
};

Другие контейнеры имеют как const, так и не const версию begin() и end(), соответствующую запрашиваемой вами подписи. std::set не имеет этого. Я не уверен, какой самый простой обходной путь для этого будет, хотя.

Тем не менее, sizeof(char) может быть sizeof(long). Самый простой способ гарантировать, что типы yes и no имеют разный размер, - это использовать ссылки на массивы разных размеров для одного и того же типа, например ::

typedef char (&yes)[1];
typedef char (&no)[2];
...
enum { value = sizeof(some_expression) == sizeof(yes) };
2 голосов
/ 11 февраля 2012

Работает для vector, но не для set, поскольку последний возвращает const_iterator для begin / end функций.Изменить:

typename T::iterator(T::*)(), 

на:

typename T::const_iterator(T::*)() const, 
...