如何强制运算符>>(C <T>)重载以匹配容器?

plo*_*ong 0 c++ containers templates operator-overloading extraction-operator

我希望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;
}
Run Code Online (Sandbox Code Playgroud)

这与GCC一样编译并运行,但是甚至不能在VS2015上编译.后者>>iss >> vi;语句中的运算符与第一个基类型重载进行匹配,从而触发其他编译错误.如何编写operator>>()非容器类型的模板和容器类型的模板(无需专门针对每种容器类型)与GCC和VS2015编译?

Bar*_*rry 5

operator>>您编写的重载是C带有单个模板参数(T)的模板模板class ().但是,std::vector声明为:

template<
    class T,
    class Allocator = std::allocator<T>
> class vector;
Run Code Online (Sandbox Code Playgroud)

第二个模板参数可能是默认的,但它仍然存在.因此,std::vector<uint32_t>无法比拟的C<T>-所以唯一可行的过载是你写的,这不能编译,因为你无法分配通用函数模板std::uintmax_tvector.

为了让你的函数接受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;
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常令人不满意的解决方案.真的,我们希望匹配任何容器,我们可以使用SFINAE.由于这是C++ 03,最简单的方法是编写一个类型特征,判断某个类型是否有一个名为的typedef 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));
};
Run Code Online (Sandbox Code Playgroud)

并添加我们的方便enable_if:

template <bool, typename >
struct enable_if { };

template <typename T>
struct enable_if<true, T> { 
    typedef T type;
};
Run Code Online (Sandbox Code Playgroud)

并坚持返回类型:

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;
}
Run Code Online (Sandbox Code Playgroud)

你必须!is_container<T>::value为另一个重载执行相反的(),这样它们就不会模糊不清.