Как заставить оператор перегрузки (C & lt; T & gt;) соответствовать контейнеру?

Я бы хотел operator>>() перегрузки для любого «базового» типа и для любого типа контейнера. Это то, что я до сих пор:

typedef uintmax_t my_t;
template <typename T>
std::istringstream &operator>>(std::istringstream &iss, T &v)
{
static my_t um = 6009;
v = um++;
return iss;
}

template <template <class> class C, typename T>
std::istringstream &operator>>(std::istringstream &iss, C<T> &c)
{
for (typename C<T>::iterator it = c.begin(); it != c.end(); ++it)
iss >> *it;
return iss;
}

int main()
{
std::vector<uint32_t> vi(3);
std::istringstream iss;
iss >> vi;
for (std::vector<uint32_t>::iterator it = vi.begin(); it != vi.end(); ++it)
std::cout << *it << std::endl;
}

Он компилируется и работает как ожидается с GCC, но даже не компилируется на VS2015. Последний соответствует >> оператор в iss >> vi; оператор с первой перегрузкой базового типа, которая вызывает другие ошибки компиляции. Как я могу написать operator>>() шаблон для неконтейнерных типов и шаблон для контейнерных типов (без необходимости специализироваться для каждого типа контейнера), который компилируется с GCC и VS2015?

0

Решение

operator>> перегрузка, которую вы написали, предназначена для шаблона шаблона класса (C) с одним аргументом шаблона (T). Тем не мение, std::vector объявлен как:

template<
class T,
class Allocator = std::allocator<T>
> class vector;

Второй аргумент шаблона может быть установлен по умолчанию, но он все еще там. В качестве таких, std::vector<uint32_t> не может соответствовать C<T> — поэтому единственной приемлемой перегрузкой является шаблон обобщенной функции, который вы написали, который не скомпилируется, потому что вы не можете назначить std::uintmax_t к vector,

Для того, чтобы получить вашу функцию, чтобы принять vector, вам нужно соответствовать объявлению шаблона шаблона — это значит, взять аргумент второго типа:

template <template <class, class> class C, typename T1, typename T2>
std::istringstream &operator>>(std::istringstream &iss, C<T1,T2> &c)
{
for (typename C<T1,T2>::iterator it = c.begin(); it != c.end(); ++it)
iss >> *it;
return iss;
}

Это довольно неудовлетворительное решение, хотя. На самом деле мы хотим сопоставить все, что является контейнером, что мы можем сделать с помощью SFINAE. Поскольку это C ++ 03, самой простой вещью было бы написать характеристику типа для того, имеет ли какой-либо тип определение типа с именем iterator:

template <typename T>
struct is_container {
typedef char yes;

struct no {
char _[2];
};

template <typename U>
static yes test( typename U::iterator* );

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

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

И добавьте наш удобный enable_if:

template <bool, typename >
struct enable_if { };

template <typename T>
struct enable_if<true, T> {
typedef T type;
};

И добавьте это в тип возврата:

template <typename C>
typename enable_if<
is_container<C>::value,
std::istringstream&
>::type
operator>>(std::istringstream &iss, C& c)
{
for (typename C::iterator it = c.begin(); it != c.end(); ++it)
iss >> *it;
return iss;
}

Тебе придется сделать наоборот (!is_container<T>::value) для другой перегрузки, чтобы они не были неоднозначными.

5

Другие решения


По вопросам рекламы [email protected]