有没有比存在allocator_type更好的方法来区分可调整大小的容器

Is there a better way to distinguish resizable containers than presence of allocator_type?

本文关键字:可调整 方法 存在 allocator 更好 type 有没有      更新时间:2023-10-16

>我有模板重载用于operator>>(),我需要区分可以调整大小的容器,例如vector,和不能调整大小的容器,例如array。我目前只是使用allocator_type特征的存在(请参阅下面的代码)--它工作得很好--但想知道是否有更明确的方法来测试它。

template <class T>
struct is_resizable {
    typedef uint8_t yes;
    typedef uint16_t no;
    template <class U>
    static yes test(class U::allocator_type *);
    template <class U>
    static no test(...);
    static const bool value = sizeof test<T>(0) == sizeof yes;
};
template <typename C>
typename boost::enable_if_c<
    boost::spirit::traits::is_container<C>::value && is_resizable<C>::value,
    istream &
>::type
operator>>(istream &ibs, C &c)
{
    c.resize(ibs.repeat() == 0 ? c.size() : ibs.repeat());
    for (typename C::iterator it = c.begin(); it != c.end(); ++it)
    {
        C::value_type v;
        ibs >> v;
        *it = v;
    }
    return ibs;
}
template <typename C>
typename boost::enable_if_c<
    boost::spirit::traits::is_container<C>::value && !is_resizable<C>::value,
    istream &
>::type
operator>>(istream &ibs, C &c)
{
    for (typename C::iterator it = c.begin(); it != c.end(); ++it)
        ibs >> *it;
    return ibs;
}
如果你想

测试一个容器是否resize -able,你可能应该检查它是否具有resize()功能。在 C++03 中,这看起来像:

template <typename T>
class has_resize
{
private:
    typedef char yes;
    struct no {
        char _[2];
    };
    template <typename U, U>
    class check
    { };
    template <typename C>
    static yes test(check<void (C::*)(size_t), &C::resize>*);
    template <typename C>
    static no test(...);
public:
    static const bool value = (sizeof(test<T>(0)) == sizeof(yes));
};

是的。您需要定义/使用自定义特征(就像boost::spirit::traits一样)。

分配器的存在与否并不能真正告诉您容器是否是固定大小的。非标准容器可能根本没有allocator_type关联类型,但仍允许resize(...)

事实上,由于您有效地断言了一个允许的概念

C::resize(size_t)

你可以只使用表达式SFINAE

Modern C++ 有一个非常简洁的方式:

template <typename T, typename = int>
struct resizable : std::false_type {};
template <typename T>
struct resizable <T, decltype((void) std::declval<T>().resize(1), 0)> : std::true_type {};

演示

现在,如果您不需要在成员函数和名称为 resize 的成员变量之间消除歧义,则可以编写上述decltype如下:

decltype( (void) &T::resize, 0 )

请注意,强制转换为 void 是为了处理类型重载逗号运算符并且泛化失败的情况(因此这只是一个比抱歉更好的安全策略)

感谢@Jarod42对另一个问题的帮助,我有一个适用于 C++98、C++03 和 C++11 的解决方案; g++ 和 VS2015。另外,对于问题儿童,std::vector<bool>.

#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)                
    template <typename U>                                                    
    class traitsName                                                         
    {                                                                        
    private:                                                                 
        typedef boost::uint8_t yes; typedef boost::uint16_t no;              
        template<typename T, T> struct helper;                               
        template<typename T> static yes check(helper<signature, &funcName>*);
        template<typename T> static no check(...);                           
    public:                                                                  
        static const bool value = sizeof check<U>(0) == sizeof(yes);         
    }
DEFINE_HAS_SIGNATURE(has_resize_1, T::resize, void (T::*)(typename T::size_type));
DEFINE_HAS_SIGNATURE(has_resize_2, T::resize, void (T::*)(typename T::size_type, 
    typename T::value_type));

这是它的使用方式,如下所示。请注意,将检查resize()has_resize_1has_resize_2成员函数签名。这是因为在 C++11 之前,resize()有一个带有两个参数的签名,最后一个带有默认值;截至 C++11,它有两个签名 - 一个具有一个参数,另一个具有两个参数。此外,VS2015显然有三个签名 - 以上所有。解决方案只是始终检查两个签名。

可能有一种方法可以将这两个检查组合成一个类型特征,例如 has_resize<C>::value .如果你知道,告诉我。

template <typename T>
typename boost::enable_if_c<
    !boost::spirit::traits::is_container<T>::value,
    xstream &>::type
    operator>>(xstream &ibs, T &b)
{
    return ibs;
}
template <typename C>
typename boost::enable_if_c<
    boost::spirit::traits::is_container<C>::value &&
    (has_resize_1<C>::value || has_resize_2<C>::value),
    xstream &
>::type
operator>>(xstream &ibs, C &c)
{
    typename C::value_type v;
    ibs >> v;
    return ibs;
}
template <typename C>
typename boost::enable_if_c<
    boost::spirit::traits::is_container<C>::value &&
    !(has_resize_1<C>::value || has_resize_2<C>::value),
    xstream &
>::type
operator>>(xstream &ibs, C &c)
{
    typename C::value_type v;
    ibs >> v;
    return ibs;
}