【问题标题】:Generic STL-Compatible Histogram in C++11C++11 中的通用 STL 兼容直方图
【发布时间】:2012-02-08 19:39:47
【问题描述】:

这是我第一次尝试使用 GCC 4.6 测试的 C++ 中的通用直方图模板函数。但是,我想将dense_histogram() 和sparse_histogram() 合并到一个通用的通用函数模板中。问题是特定于 dense 的构造函数 H h(n, 0) 在 sparse 版本 H h 中既未定义也不相关。有没有办法以一些聪明的 C++ 常规方式或静态地通过 Boost Type.Traits (#include <boost/type_traits.hpp>) 使用条件编译来解决这个问题?

#include <algorithm>
#include <limits>
#include <algorithm>
#include <vector>
#include <unordered_map>

namespace std
{

/*!
 * \em Dense Histogram of \p a.
 *
 * \tparam V is Value Type.
 * \tparam C is Count (Bin) Type.
 * \tparam H is Histogram Storage Type, typically a vector.
 *
 * \param[in] x is a set of the input data set
 */
template <class V, class C = size_t, class H = vector<C> >
inline
H dense_histogram(const V & x)
{
    typedef typename V::value_type E; // element type
    size_t n = (static_cast<C>(1)) << (8*sizeof(E)); // maximum number of possible elements for dense variant
    H h(n, 0);                       // histogram
    C bmax = 0;                      // bin max
    for_each(begin(x), end(x),  // C++11
             [&h, &bmax] (const E & e) { // value element
                 h[e]++;
                 bmax = std::max(bmax, h[e]);
             });
    return h;
}
template <class V, class H = vector<size_t> > H make_dense_histogram(const V & x) { return dense_histogram<V, size_t, H>(x); }

/*!
 * \em Sparse Histogram of \p a.
 *
 * \tparam V is Value Type.
 * \tparam C is Count (Bin) Type.
 * \tparam H is Histogram Structure Type, typically a unordered_map.
 *
 * \param[in] x is a set of the input data set
 */
template <class V, class C = size_t, class H = unordered_map<typename V::value_type, C> >
inline
H sparse_histogram(const V & x)
{
    typedef typename V::value_type E; // element type
    H h;                        // histogram
    C bmax = 0;                 // bin max
    for_each(begin(x), end(x), // C++11
             [&h,&bmax] (const E & e) { // value element
                 h[e]++;
                 bmax = std::max(bmax, h[e]);
             });
    return h;
}
template <class V, class H = unordered_map<typename V::value_type, size_t> > H make_sparse_histogram(const V & x) { return sparse_histogram<V, size_t, H>(x); }

}

运行使用

【问题讨论】:

  • 不能用dense map来实现sparse map吗?您所做的只是切换容器的默认模板参数。我只需要一个 create_histogram 函数,在容器上进行模板化。

标签: c++ templates generics histogram conditional-compilation


【解决方案1】:

我认为您应该只将公共部分放在第三个函数中,留下 dense_histogram 和 sparse_histogram 创建 h 并调用该实现函数:

template <class V, class C = size_t, class H>
inline void histogram_impl(const V & x, H& h) {
    typedef typename V::value_type E; // element type
    C bmax = 0;                      // bin max
    for_each(begin(x), end(x),  // C++11
             [&h, &bmax] (const E & e) { // value element
                 h[e]++;
                 bmax = std::max(bmax, h[e]);
             });
    return h;
}
template <class V, class C = size_t, class H = vector<C> >
inline H dense_histogram(const V & x) {
    typedef typename V::value_type E; // element type
    size_t n = (static_cast<C>(1)) << (8*sizeof(E)); // maximum number of possible elements for dense variant
    H h(n, 0);                       // histogram
    histogram_impl(x, h);
    return h;
}
template <class V, class C = size_t, class H = unordered_map<typename V::value_type, C> >
inline H sparse_histogram(const V & x) {
    H h;                        // histogram
    histogram_impl(x, h);
    return h;
}

但是,既然您提出了要求:当您正在处理容器时,我会假设它们有一个便宜的移动,因此您可以定义一个创建特征来生成您的容器并将其移动到您的局部变量中。然后您可以编写自己的检测适当的构造函数,如下所示:

template<typename T> struct has_explicit_length_constructor{
private:
   template<typename U>
   decltype(U(0, 0), void(), std::true_type()) test(int x);
   template<typename>
   std::false_type test(...);
  typedef decltype(test<T>(0)) constant_type;
public:
   constexpr bool value = constant_type::value;
};

template<class H, bool B = has_explicit_length_constructor<H>::value> struct histogram_creation_trait;
template<class H> struct histogram_creation_trait<H, true> {
  static H create()  {
    size_t n = (static_cast<C>(1)) << (8*sizeof(typename V::value_type));
    return H(n, 0);  
  }
};
template<class H> struct histogram_creation_trait<H, false>
{ static H create()  { return H(); } };

template <class V, class C = size_t, class Ht>
inline void histogram_impl(const V & x, H& h, Trait) {
    typedef typename V::value_type E; // element type
    C bmax = 0;                      // bin max
    H h = histogram_creation_trait<H>::create();
    for_each(begin(x), end(x),  // C++11
             [&h, &bmax] (const E & e) { // value element
                 h[e]++;
                 bmax = std::max(bmax, h[e]);
             });
    return h;
}
template <class V, class H = vector<size_t> > H make_dense_histogram(const V & x) { return histogram_impl<V, size_t, H>(x); }
template <class V, class H = unordered_map<typename V::value_type, size_t> > H make_sparse_histogram(const V & x) { return histogram_impl<V, size_t, H>(x); }

另一方面:将您自己的方法添加到 std 是标准的 UB ([namespace.std] $17.6.4.2.1 p1):

除非另有说明,否则如果 C++ 程序向命名空间 std 或命名空间 std 内的命名空间添加声明或定义,则其行为未定义。只有当声明依赖于用户定义的类型并且特化满足原始模板的标准库要求并且没有明确禁止时,程序才能将任何标准库模板的模板特化添加到命名空间 std。

【讨论】:

  • 好的,我改一下命名空间。
猜你喜欢
  • 2014-07-19
  • 2016-01-13
  • 2013-07-02
  • 2011-07-24
  • 2012-10-07
  • 2023-03-26
  • 2013-06-25
  • 1970-01-01
相关资源
最近更新 更多