0

我如何从结构数组初始化 std::vector,其中结构包含不同类型的联合。换句话说,数组用于存储特定类型的多个值,可以是 int、char* 等。

到目前为止,这是我的解决方案,但我正在寻找更好的方法:

vector<int>如果它存储ints,则convert 函数返回 a ,如果存储 s,vector<std::string>则返回 a char*

下面的 Value 类型是一个包含名为 value 的联合的结构。下面的 Container 类指向此类值的缓冲区。

// union member getter
class Getter
{
public:

    void operator()(int8_t& i, const Value& value)
    {
        i = value.value.i;
    }

    void operator()(std::string& s, const Value& value)
    {
       s = std::string(value.value.s);
    }

    ...
};

template<class T>
std::vector<T> convert(Container* container)
{
    std::vector<T> c;
    c.reserve(container->nrOfValues);
    Getter g;
    for(int i=0;i<container->nrOfValues;i++)
    {
        T value;
        g(value, container->values[i]);
        c.push_back(value);
    }
    return c;
}
4

2 回答 2

1

如果你喜欢一些模板魔法,你可以用稍微不同的方式来做:

// Source union to get data from
union U
{
  int i;
  char* s;
  double d;
};

// Conversion type template function (declared only)
template <class T> T convert(const U& i_u);

// Macro for template specializations definition
#define FIELD_CONV(SrcType, DestField)\
template <> SrcType convert(const U& i_u)\
{ auto p = &DestField; return i_u.*p; }

// Defining conversions: source type -> union field to get data from
FIELD_CONV(int, U::i)
FIELD_CONV(std::string, U::s)
FIELD_CONV(double, U::d)

// Get rid of macro that not needed any more - just for macro haters ;-)
#undef FIELD_CONV

// Usage
template<class T> std::vector<T> convert(Container* container)
{
   std::vector<T> c;
   c.reserve(container->nrOfValues);
   for(int i = 0; i < container->nrOfValues; ++i)
     c.push_back(convert<T>(container->values[i])); 
   return c;
} 

这种方法的优点——它简短、简单且易于扩展。当您向联合添加新字段时,您只需编写另一个FIELD_CONV()定义。

编译示例在这里

于 2012-09-13T10:56:24.713 回答
1

您的问题是联合为每个值提供不同的名称,这导致需要将名称转换为类型的函数,例如 Getter::operator() 返回类型并获取联合的命名成员。

你对此无能为力。您可以在每个项目上保存变量声明和复制/字符串构造函数,仅此而已。

如果您无法修改原始结构,则可以使用一组长度默认值(必须传入)初始化向量,然后使用 getter 进行迭代:

vector<T> v(length, defaultValue);
typename vector<T>::iterator iter = vec.begin();
for(int index = 0; *iter != vec.end() && index < length; ++iter, ++index) {
  converter(*iter, array[index]);
}

请注意,这在迭代索引和迭代器以及验证两者在发生事故时仍然有效时开始变得很麻烦......

如果您可以修改原始结构:

class Ugly { // or struct, it doesn't matter
public:
  union {
    char* s;
    int i;
  } value;

  Ugly(char* s) {
    value.s = s;
  }

  Ugly (const int& i) {
    value.i = i;
  }

  operator std::string() const {
    return std::string(value.s);
  }

  operator int() const {
    return value.i;
  }
};

然后你的 for 循环变成:

for(int i=0;i<container->nrOfValues;i++)
{
    c.push_back(container->values[i]);
}

注意:您可以创建向量并将其作为参数传递给复制函数,因为它涉及在返回期间复制数据。

于 2012-09-12T17:28:23.690 回答