是否有一种更简洁的方法可以在 c++(11) 中复制具有多类型值的 unordered_map

Posted

技术标签:

【中文标题】是否有一种更简洁的方法可以在 c++(11) 中复制具有多类型值的 unordered_map【英文标题】:Is there a cleaner way to replicate an unordered_map with multi-type values in c++(11) 【发布时间】:2013-11-20 02:30:33 【问题描述】:

基本思想是得到一个存储不同类型值的unordered_map。我要做的是为 OpenGL 统一缓冲区对象创建一个易于访问的对象。最终产品看起来像:

UBO ubo =  "Uniforms", "translation", "scale", "rotation", "enabled" ;
ubo["scale"]       = 0.5f;
ubo["translation"] =  0.1f, 0.1f, 0.0f ;
ubo["rotation"]    =  90.0f, 0.0f, 0.0f, 1.0f ;
ubo["enabled"]     = GL_TRUE; 

在我的 UBO 类中,我重载了 operator[]:

struct UBOData;

class UBO

    std::unordered_map<std::string,UBOData>
    ...
    public: 
    UBOData &operator[](std::string key)
    
        UBOData data = new UBOData();
        dataMap.emplace(key, data);
        return data;
    

    const UBOData& operator[](std::string key)
    
        return const_cast<UBOData&>(*this)[key];
    
;

我正在使用 UBOData 来存储不同的数据类型。鉴于 c++ 世界中的“正确”,这就是我的信心减弱的地方。

.
.
.
struct UBOData

    enum ReturnType Undefined, rInt, rFloat, rDouble;

    void       *value;
    ReturnType type;
    int &operator=(int lhs);
    float &operator=(float lhs);
    double &operator=(double lhs);
;

我已经截断了这个例子的类型,没有 std::array 类型。另请注意,我使用 void * 来存储值并告诉我需要重新考虑我的设计。我当然这样做,这就是我在这里的原因:)

int &UBOData::operator=(int lhs)

    if (type == Undefined)  type = rInt;  else  assert(type == rInt); 
    value = new int(lhs);
    int &rValue = *((int*)value);
    return rValue;


float &UBOData::operator=(float lhs)

    if (type == Undefined)  type = rFloat; 
    else  assert(type == rFloat); 

    value = new float(lhs);
    float &rValue = *((float*)value);
    return rValue;


double &UBOData::operator=(double lhs)

    if (type == Undefined)  type = rDouble; 
    else  assert(type == rInt); 

    value = new double(lhs);
    double &rValue = *((double*)value);
    return rValue;

我试图用类型检查来包装 void*,但有没有更好的方法来获得没有 void * 的多类型映射?

注意:我在 Windows 上使用 VS2013,在 Mac 和 Linux 上使用 clang。

【问题讨论】:

您是否考虑过拥有类型的多态层次结构,存储(智能)指向基类的指针?然后,您可以通过在每个元素上调用 clone() 方法来“复制”(复制)地图。或者,您可以考虑 boost 变体或 boost any。 @TonyD 感谢您的建议。它引导我找到这个链接:two-sdg.demon.co.uk/curbralan/papers/ValuedConversions.pdf 与您的问题无关:您的 operator [] 的 const 版本根本不是 const 。这没有意义。 @c.r.看起来像是尝试使用失败的非 const 版本。 @C.R.你说的对。那是复制粘贴错误。谢谢。 【参考方案1】:

boost::variantboost::any

如果您不能或不会使用 boost,请阅读他们的做法。

我自己会选择variant

【讨论】:

是的,从上面 TonyD 的评论中,我正在研究这两个结构。我还发现了另一个与该主题相关的链接,其中包含一个有趣的花絮***.com/a/6044720/1670072【参考方案2】:

绝对是boost::variant。这就是它的目的。这是使用您的代码的small example:

#include <unordered_map>
#include <vector>
#include <boost/variant.hpp>

class UBO

    using UBOData = boost::variant<float, std::vector<float>>;
    std::unordered_map<std::string, UBOData> dataMap;
    public: 
    UBO() : dataMap()
    UBOData &operator[](const std::string& key)
    
        return dataMap[key];
    
;

int main()

   UBO ubo;
   ubo["scale"]       = 0.5f;
   ubo["translation"] = std::vector<float> 0.1f, 0.1f, 0.0f ;
   ubo["rotation"]    = std::vector<float> 90.0f, 0.0f, 0.0f, 1.0f ;

如果您想要 0.1f, 0.1f, 0.0f 语法而不输入 std::vector&lt;float&gt; 等,您可能需要某种类型的代理来处理初始化列表。

【讨论】:

谢谢。我会更多地研究提升变体。 boost::variant 可以接受两个以上的模板参数吗? @SethHays:是的,最多 20 个。

以上是关于是否有一种更简洁的方法可以在 c++(11) 中复制具有多类型值的 unordered_map的主要内容,如果未能解决你的问题,请参考以下文章

是否有一种简洁的方法可以为同一个扩展定义两个或多个 bash 别名?

有没有一种更简洁的方法来获得第一次出现的东西?

在 Swift 中是不是有一种简洁的方法可以将完成块附加到 NSURLSessionDataDelegate 回调?

记录--一种更现代的深浅拷贝方法

SQL Server 代理作业在失败时通知多个操作员

SpriteKit:场景完成呈现时回调?