Вопрос

Следующий мой код должен определить, T имеет begin и end методы:

template <typename T>
struct is_container
{
    template <typename U, typename U::const_iterator (U::*)() const,
                          typename U::const_iterator (U::*)() const>
    struct sfinae {};

    template <typename U> static char test(sfinae<U, &U::begin, &U::end>*);
    template <typename U> static long test(...);

    enum { value = (1 == sizeof test<T>(0)) };
};

И вот небольшой тестовый код:

#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <map>

int main()
{
    std::cout << is_container<std::vector<std::string> >::value << ' ';
    std::cout << is_container<std::list<std::string> >::value << ' ';
    std::cout << is_container<std::set<std::string> >::value << ' ';
    std::cout << is_container<std::map<std::string, std::string> >::value << '\n';
}

В g++ 4.5.1 результат будет такой: 1 1 1 1.Однако в Visual Studio 2008 результат выглядит следующим образом: 1 1 0 0.Я сделал что-то не так или это просто ошибка VS 2008?Может ли кто-нибудь протестировать на другом компиляторе?Спасибо!

Это было полезно?

Решение 4

У Стефана Т. Лававе это сказать:

Обратите внимание, что технически запрещено принимать адрес функции стандартной библиотеки. (Они могут быть перегружены, делая &foo::bar неоднозначно, и они могут иметь дополнительные аргументы по умолчанию, побеждая попытки устранить static_cast.)

Так что я думаю, я собираюсь использовать более простую версию, которая проверяет только для вложенного const_iterator тип.

Другие советы

Итак, вот как я отлаживаю эти вещи.

Сначала закомментируйте отрицательную альтернативу, чтобы получить ошибку, а не просто несоответствие.Затем попробуйте создать экземпляр типа, который вы помещаете в функцию, с одним из элементов, который не работает.

На этом этапе мне удалось создать экземпляр вашего объекта sfinae, но он все еще не работал.«Это позволяет мне знать, что это ошибка против VS, поэтому вопрос в том, как ее исправить». - ОБС

Кажется, у VS проблемы с SFINAE, когда все сделано так, как вы. Конечно, это так! Это работает лучше, когда вы обертываете свой объект sfinae.Я сделал это так:

template <typename U, typename it_t = typename U::const_iterator >
struct sfinae 
{
  // typedef typename U::const_iterator it_t; - fails to compile with non-cont types.  Not sfinae
  template < typename U, typename IT, IT (U::*)() const, IT (U::*)() const >
  struct type_ {};

  typedef type_<U,it_t,&U::begin,&U::end> type;
};

Все еще не работало, но, по крайней мере, я получил полезное сообщение об ошибке:

error C2440: 'specialization' : cannot convert from 'overloaded-function' to 'std::_Tree_const_iterator<_Mytree> (__thiscall std::set<_Kty>::* )(void) const'

Это позволяет мне знать, что &U::end недостаточно для VS (ЛЮБОЙ компилятор), чтобы иметь возможность определить, какой end() мне нужен. static_cast исправляет это:

  typedef type_<U,it_t,static_cast<it_t (U::*)() const>(&U::begin),static_cast<it_t (U::*)() const>(&U::end)> type;

Соберите все это обратно и запустите на нем тестовую программу... успех с VS2010.Возможно, вы обнаружите, что static_cast — это все, что вам нужно, но я предоставил вам это выяснить.

Я полагаю, что реальный вопрос теперь в том, какой компилятор прав?Я делаю ставку на то, что будет последовательным:г++. Укажи на мудрого: НИКОГДА предположим, что я сделал тогда.

Редактировать:Боже... Вы неправы!

Исправленная версия:

template <typename T>
struct is_container
{
    template <typename U, typename it_t = typename U::const_iterator > 
    struct sfinae 
    {
      //typedef typename U::const_iterator it_t;
      template < typename U, typename IT, IT (U::*)() const, IT (U::*)() const >
      struct type_ {};

      typedef type_<U,it_t,static_cast<it_t (U::*)() const>(&U::begin),static_cast<it_t (U::*)() const>(&U::end)> type;
    };

    template <typename U> static char test(typename sfinae<U>::type*);
    template <typename U> static long test(...);

    enum { value = (1 == sizeof test<T>(0)) };
};



#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <map>

int main()
{
    std::cout << is_container<std::vector<std::string> >::value << ' ';
    std::cout << is_container<std::list<std::string> >::value << ' ';
    std::cout << is_container<std::set<std::string> >::value << ' ';
    std::cout << is_container<std::map<std::string, std::string> >::value << ' ';
    std::cout << is_container<bool>::value << '\n';
}

-- Приведенная выше отладка разумна, но предположение о компиляторе было ошибочным.G++ должен был потерпеть неудачу по причине, которую я подчеркнул выше.

Почему вы собираетесь все эти усилия? Если вы хотите проверить, U::begin() существует, почему бы не попробовать?

template <typename T>
struct is_container
{
    template <typename U> static char test(U* u,
       typename U::const_iterator b = ((U*)0)->begin(),
       typename U::const_iterator e = ((U*)0)->end());
    template <typename U> static long test(...);

    enum { value = (1 == sizeof test<T>(0)) };
};

В дополнение к проверке на существование U::begin() а также U::end(), это также проверяет, возвращают ли они что-то, что является конвертируемым в const_iterator. Отказ Это также позволяет избежать подводной ловушки, выделенной Стефом Т. Лававеем, используя выражение вызовов, которое необходимо поддерживать, вместо того, чтобы предположить определенную подпись.

править] Извините, это полагалось на экземпляр шаблона VC10. Лучший подход (ставит проверку существования в типов аргументов, которые делать участвовать в перегрузке):

template <typename T> struct is_container
{
    // Is.
    template <typename U>
    static char test(U* u, 
                     int (*b)[sizeof(typename U::const_iterator()==((U*)0)->begin())] = 0,
                     int (*e)[sizeof(typename U::const_iterator()==((U*)0)->end())] = 0);
    // Is not.
    template <typename U> static long test(...);

    enum { value = (1 == sizeof test<T>(0)) };
};

С C ++ 11, теперь есть лучшие способы обнаружения этого. Вместо того, чтобы полагаться на подпись функций, мы просто называем их в контексте выражения Sfinae:

#include <type_traits> // declval

template<class T>
class is_container{
  typedef char (&two)[2];

  template<class U> // non-const
  static auto test(typename U::iterator*, int)
      -> decltype(std::declval<U>().begin(), char());

  template<class U> // const
  static auto test(typename U::const_iterator*, long)
      -> decltype(std::declval<U const>().begin(), char());

  template<class>
  static two  test(...);

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

Живой пример на идее. То int а также long Параметры только для устранения разрешения перегрузки, когда контейнер предлагает как (или если iterator является typedef const_iterator iterator, подобно std::set разрешено) - буквальный 0 имеет тип int и заставляет первую перегрузку выбрать.

Это, вероятно, должно быть комментарий, но у меня нет достаточно очков

@Msalters.

Хотя ваш is_container Работает (почти), и я сам использовал свой код, я обнаружил две проблемы в этом.

Во-первых, этот тип deque<T>::iterator обнаружен как контейнер (в GCC-4.7). Кажется, что deque<T>::iterator имеет begin/end члены и const_iterator тип определен.

2-я проблема в том, что этот код недействителен в соответствии с GCC DEV. I QUTE: Значения аргументов по умолчанию не являются частью типа функций и не участвуют в вычете. Отказ Видеть GCC BUG 51989.

Я сейчас использую это (Только C ++ 11) для is_container<T>:

template <typename T>
struct is_container {
    template <
        typename U,
        typename S = decltype (((U*)0)->size()),
        typename I = typename U::const_iterator
    >
    static char test(U* u);
    template <typename U> static long test(...);
    enum { value = sizeof test<T>(0) == 1 };
};
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top