Я бы хотел 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?
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
) для другой перегрузки, чтобы они не были неоднозначными.