【问题标题】:How could I uncast a void* that I don't know the data type?我怎样才能取消我不知道数据类型的 void* ?
【发布时间】:2020-04-24 21:11:42
【问题描述】:

我有一个包含两个不同std::multimap 的结构。我想做这样的事情,但是,我怎样才能取消我不知道数据类型的void*

struct Data{
  std::multimap<uint64_t,uint64_t, std::greater<uint64_t>> b;
  std::multimap<uint64_t,uint64_t, std::less<uint64_t>> s;
};

Data data;

void do_something(bool c){
    void* pointer;
    uint64_t cumulative = 0;
    if(c){
        pointer = &data.b;
    } else {
        pointer = &data.s;
    }
    /*      Here I don't know if pointer is
    *           std::multimap<uint64_t,uint64_t, std::greater<uint64_t>>
    *           or
    *           std::multimap<uint64_t,uint64_t, std::less<uint64_t>>
    */
    for(auto it = (*pointer).begin(); it != (*pointer).end(); ++it){ 
        cumulative += it->second;
    }

}

谢谢

【问题讨论】:

  • 不要通过void*,写一个小函数模板做积累(或者看看std::accumulate是不是已经不适合你的需要了)。
  • std::variantstd::any
  • "我怎样才能取消一个我不知道数据类型的 void*?"你没有。

标签: c++ void-pointers


【解决方案1】:

如果没有其他信息,您将不会知道底层类型是什么。因此,将void* 转换为任何类型的指针类型都容易出错。

您可以使用不同的策略来处理您的情况。

template <typename Iter>
uint64_t do_something(Iter start, Iter end)
{
   uint64_t cumulative = 0;
   for(auto it = start; it != end; ++it){ 
      cumulative += it->second;
   }
   return cumulative;
}

void do_something(bool c)
{
   uint64_t cumulative = 0;
   if(c)
   {
      cumulative = do_something(data.b.begin(), data.b.end());
   }
   else
   {
      cumulative = do_something(data.s.begin(), data.s.end());
   }
}

您可以使用std::accumulate 来简化第一个功能。

template <typename Iter>
uint64_t do_something(Iter start, Iter end)
{
   return std::accumulate(start, end, 0);
}

【讨论】:

    【解决方案2】:

    如何取消转换我不知道数据类型的 void*?

    你不能这样做。

    您只能转换为您知道的类型。

    对于这样的事情,你可以编写一个在任何范围内操作的函数模板:

    template<class Range>
    void do_something_template(Range& r){
        // do something
    }
    
    void do_something(bool c){
        uint64_t cumulative = c
            ? do_something_template(data.b)
            : do_something_template(data.s);
    

    也就是说,在这种特殊情况下,您正在执行的操作已经有一个标准算法:std::accumulate,无需重写。

    【讨论】:

    • 谢谢! std::accumulate 可能对我有用
    【解决方案3】:

    您不能取消引用 void 指针,但在您的情况下,您可以这样做:

    template<typename Container>
    uint64_t do_something_helper(const Container& container)
    {
        uint64_t cumulative = 0;
        for (const auto& item : container) {
            cumulative += item.second;
        }
        return cumulative;
    }
    
    void do_something(bool c)
    {
        uint64_t cumulative = [&]() {
            if (c) {
                return do_something_helper(data.b);
            }
            else {
                return do_something_helper(data.s);
            }
        }();
    
        // Do something with cumulative
    }
    

    此外。这是 C++17 中的std::any

    void do_something(bool c)
    {
        std::any container;
        if (c) {
            container = data.b;
        }
        else {
            conainter = data.s;
        }
    
        try {
            auto* pCont = std::any_cast<decltype(data.b)>( &container );
            for ( ... ) {
                // compute cumulative
            }
        }
        catch(const std::bad_any_cast&) {
            auto* pCont = std::any_cast<decltype(data.s)>( &container );
            for ( ... ) {
                // compute cumulative
            }
        }
    }
    

    但对我来说,第一个解决方案看起来比第二个解决方案好一点:)

    【讨论】:

    • 这个想法是不重复代码片段。我认为第一个选项对我有用,谢谢!
    【解决方案4】:

    只有void*,你不能。但是,这里有两种可能的解决方案(具有不同程度的侵入性):

    1. 您还可以通过某种方式对类型进行编码,例如使用枚举,然后使用它来static_cast 回到具体类型。
    struct A {};
    struct B {};
    
    enum class Type
    {
        A,
        B,
    };
    
    void do_something(bool c)
    {
        A a;
        B b;
    
        void* pointer;
        Type type;
    
        if (c)
        {
            pointer = &a;
            type = Type::A;
        }
        else
        {
            pointer = &b;
            type = Type::B;
        }
    
        if (type == Type::A)
        {
            A* concrete = static_cast<A*>(pointer);
            // Use concrete here as A.
        }
        else if (type == Type::B)
        {
            B* concrete = static_cast<B*>(pointer);
            // Use concrete here as B.
        }
    }
    
    1. 使您的类型具有多态性(使用虚函数和公共基类)。而不是 void* 使 pointer 指向公共基类的指针,您可以将 dynamic_cast 指向具体类型。

    然而我会注意到,通常在一个设计良好的程序中,类型的多态使用不需要知道具体的类型。研究使用虚函数或std::variant

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-25
      • 1970-01-01
      • 2022-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-13
      相关资源
      最近更新 更多