【问题标题】:Is there a better way to distinguish resizable containers than presence of allocator_type?有没有比 allocator_type 更好的方法来区分可调整大小的容器?
【发布时间】:2015-06-21 19:43:44
【问题描述】:

我有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;
}

【问题讨论】:

    标签: c++ templates boost containers enable-if


    【解决方案1】:

    如果你想测试一个容器是否是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));
    };
    

    【讨论】:

    • 这行不通;不能保证resize 的签名实际上是void(size_t)
    • @Mehrdad,这可能已经足够满足我的需求了。
    • @user4438540:我怀疑这是否足够......至少你也想检查void(size_t, T)
    【解决方案2】:

    是的。您需要定义/使用自定义特征(例如 boost::spirit::traits are)。

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

    事实上,因为您有效地主张了一个允许

    概念
    C::resize(size_t)
    

    你可以只用表达式 SFINAE 来做这个

    【讨论】:

      【解决方案3】:

      现代 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 {};
      

      Demo

      现在,如果您不需要区分名称为resize 的成员函数和成员变量,您可以将上面的decltype 编写如下:

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

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

      【讨论】:

      • VS2015 抱怨您的解决方案“错误 C2228:'.resize' 左侧必须有类/结构/联合”和“注意:类型是 'add_rvalue_reference<_ty>::type'”。
      • @user4438540 1 2
      • 微软在您的第二个链接中说“问题已得到修复:该修复将包含在 Visual C++ 的未来版本中。” VS2015 必须是“未来版本”,因为该错误不会出现,并且不需要解决方法。因此,我认为我看到的 C2228 问题有所不同。
      • 我认为this 与我遇到的问题相同,并且在 VS2015 中似乎尚未修复。
      • @user4438540 我刚刚提到了有问题的遗留 VS 与declval。您链接的最后一个问题是正确的,尽管我相信 VS 在void_t 方面也有问题(正如 W.Brown 所说,当presenting void_t - 这就是您提到的 sn-p 的来源,请检查@ 39.00)。
      【解决方案4】:

      感谢@Jarod42 在另一个问题上的帮助,我有一个适用于 C++98、C++03 和 C++11 的解决方案; g++ 和 VS2015。另外,对于有问题的孩子,std::vector&lt;bool&gt;

      #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&lt;C&gt;::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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-18
        • 2010-09-21
        相关资源
        最近更新 更多