对存储在boost::any中的向量调用vector::size()

call vector::size() on vector stored in boost::any

本文关键字:vector 调用 size 向量 存储 boost any      更新时间:2023-10-16

我在boost::any中存储了值,并且我希望有一个函数,如果boost::any包含std::vector,该函数将返回元素数。

以下是使用示例:

int a = 42;
vector<int> v = {1,2,3,4};
vector<int> w;
boost::any aa = a;
boost::any av = v;
boost::any aw = w;
// I would like to have this function `count`
count( aa ) // return 1
count( av ) // return 4
count( aw ) // return 0
// I can do following. But I do not like the template argument.
count<int>( aa ) // return 1
count<int>( av ) // return 4
count<int>( aw ) // return 0
count<float>( aa ) // error

问题是,我不能在不指定T的情况下简单地转换为vector<T>。有办法绕过它吗?

解决方案可以使用一个中间容器:

class vector_holder_base {
public:
    virtual std::size_t size() = 0;
}
template <class T, class... Others>
class vector_holder : public vector_holder_base {
public:
    vector_holder(const std::vector<T, Others...>& val) {...}
    vector_holder(std::vector<T, Others...>&& val) {...}
    vector_holder& operator=(const std::vector<T, Others...>& val) {...}
    vector_holder& operator=(std::vector<T, Others...>&& val) {...}
    std::size_t size() override {
        return values.size();
    }
private:
    std::vector<T, Others...> values;
}

那么你所要做的就是:

boost::any aa = vector_holder<int>(a);
std::size_t count = boost::any_cast<vector_holder_base>(aa).size();

正如您所看到的,在检索大小时,您不需要知道矢量模板类型。

然而,您需要考虑向量的多个副本(当您将其传递给vector_holder时,然后当vector_holder复制到boost::any时(考虑移动语义)。

std::vector<int>std::vector<float>在运行时是不相关的类型。

boost::any类型(确切地说)将复制和提取擦除到自己的类型,不再如此。

如果您想获取不相关的(运行时)类型并类型擦除其他属性,则应该检查boost.TypeErasure,或者自己进行此类擦除。

或者,增强的any(具有类型擦除的size)可以工作。假设C++11支持:

struct sized_any;
typedef std::size_t(sizer_t*)(sized_any const*)>;
template<class ValueType>
struct make_sizer {
  sizer_t operator()() const {
    return [](sized_any const*){return 1;}
  }
};
template<class ValueType, class... Whatever>
struct make_sizer< std::vector<ValueType, Whatever...> > {
  sizer_t operator()() const {
    return [](sized_any const* n){
      // convert n to a const std::vector<ValueType, Whatever...>*
      // invoke .size()
    }
  }
};
struct sized_any : private boost::any {
  sized_any( sized_any const& o ) = default;
  sized_any( sized_any && o ) = default;
  sized_any():boost::any(), size([](sized_any const*){return 0;}) {}
  sized_any & operator=(const sized_any &) = default;
  sized_any & operator=(sized_any &&) = default;
  template<typename ValueType> sized_any(const ValueType &v):boost::any(v), sizer(make_sizer<ValueType>{}())
  {}
  template<typename ValueType> sized_any(ValueType &&v):boost::any(std::move(v)), sizer(make_sizer<ValueType>{}())
  {}
  template<typename ValueType> sized_any & operator=(const ValueType & v){
    this->boost::any::operator=(v);
    sizer=make_sizer<ValueType>{}();
    return *this;
  }
  template<typename ValueType> sized_any & operator=(ValueType && v) {
    this->boost::any::operator=(std::move(v));
    sizer=make_sizer<ValueType>{}();
    return *this;
  }
  ~sized_any() = default;
  // modifiers
  sized_any & swap(sized_any & o) { this->boost::any::swap(o); std::swap( sizer, o.sizer ); }
  std::size_t size() const { return sizer(this); }
private:
  sizer_t sizer;
};

boost::any的私有继承是阻止boost::any::swap被直接调用或其他可以改变boost::any中存储的类型的函数。您必须重新实现/转发在boost::any上操作的函数才能在sized_any上操作。

基本设计很简单。我们维护了一个boost::any,每当它的类型发生变化时,我们都会构建一个新的函数指针,可以从中提取合适的大小。sizer会将一个指向sized_any的指针带到我们的复制/赋值运算符,这是它所需要的所有状态(所以我们不需要在函数指针中存储任何状态)。

以上并不是一个完整的实现,而是一个草图。

额外的状态(函数指针)必须独立维护,所以any的无状态修改是不可能的。