c ++将参数发送给union(变量union)

时间:2016-02-08 13:59:06

标签: c++ c++11

好吧,我不知道这是怎么回事,基本上它是与params的变量联合,基本思想,(写作函数)

练习1

union Some (int le)
{
  int i[le];
  float f[le];
};

练习2

union Some
{
  int le;
  int i[le];
  float f[le];
};

阻止这项工作D: 也许是一种使用内部变量来设置长度的方法,但也不能起作用。 THX。

5 个答案:

答案 0 :(得分:9)

不,这是不可能的:{em}需要在编译时知道。

一种解决方案是使用模板联合

le

template <int N> union Some { int i[N]; float f[N]; }; 当然是编译时可评估的。

另一个解决方案是可以说更简洁

N

或基于 typedef std::vector<std::pair<int, float>> Some; 的类似解决方案。

答案 1 :(得分:2)

根据您的使用案例,您可以尝试模拟联合。

struct Some
{
    //Order is important
private:
    char* pData;
public:
    int* const i;
    float* const f;

public:
    Some(size_t len)
        :pData(new char[sizeof(int) < sizeof(float) ? sizeof(float) : sizeof(int)])
        ,i ((int*)pData)
        ,f ((float*)pData)
    {
    }

    ~Some()
    {
        delete[] pData;
    }

    Some(const Some&) = delete;
    Some& operator=(const Some&) = delete;
};

使用模板,unique_ptr和显式强制转换的替代解决方案:

//max_size_of<>: a recursive template struct to evaluate the
// maximum value of the sizeof function of all types passed as
// parameter
//The recursion is done by using the "value" of another
// specialization of max_size_of<> with less parameter types
template <typename T, typename...Args>
struct max_size_of
{
    static const std::size_t value = std::max(sizeof(T), max_size_of<Args...>::value);
};

//Specialication for max_size_of<> as recursion stop
template <typename T>
struct max_size_of<T>
{
    static const std::size_t value = sizeof(T);
};

//dataptr_auto_cast<>:  a recursive template struct that 
// introduces a virtual function "char* const data_ptr()"
// and an additional explicit cast operator for a pointer
// of the first type. Due to the recursion a cast operator 
// for every type passed to the struct is created.
//Attention: types are not allowed to be duplicate    
//The recursion is done by inheriting from of another
// specialization of dataptr_auto_cast<> with less parameter types
template <typename T, typename...Args>
struct dataptr_auto_cast : public dataptr_auto_cast<Args...>
{
    virtual char* const data_ptr() const = 0; //This is needed by the cast operator
    explicit operator T* const() const { return (T*)data_ptr(); } //make it explicit to avoid unwanted side effects (manual cast needed)
};

//Specialization of dataptr_auto_cast<> as recursion stop
template <typename T>
struct dataptr_auto_cast<T>
{
    virtual char* const data_ptr() const = 0;
    explicit operator T* const() const { return (T*)data_ptr(); }
};

//union_array<>: inherits from dataptr_auto_cast<> with the same
// template parameters. Also has a static const member "blockSize"
// that indicates the size of the largest datatype passed as parameter
// "blockSize" is used to determine the space needed to store "size"
// elements.
template <typename...Args>
struct union_array : public dataptr_auto_cast<Args...>
{
    static const size_t blockSize = max_size_of<Args...>::value;

private:
    std::unique_ptr<char[]> m_pData; //std::unique_ptr automatically deletes the memory it points to on destruction
    size_t m_size; //The size/no. of elements

public:

    //Create a new array to store "size" elements
    union_array(size_t size)
        :m_pData(new char[size*blockSize])
        ,m_size(size)
    {
    }

    //Copy constructor
    union_array(const union_array<Args...>& other)
        :m_pData(new char[other.m_size*blockSize])
        ,m_size(other.m_size)
    {
        memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
    }

    //Move constructor
    union_array(union_array<Args...>&& other)
        :m_pData(std::move(other.m_pData))
        ,m_size(std::move(other.m_size))
    {
    }


    union_array& operator=(const union_array<Args...>& other)
    {
        m_pData = new char[other.m_size*blockSize];
        m_size = other.m_size;
        memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
    }

    union_array& operator=(union_array<Args...>&& other)
    {
        m_pData = std::move(other.m_pData);
        m_size = std::move(other.m_size);
    }

    ~union_array() = default;

    size_t size() const
    {
        return m_size;
    }

    //Implementation of dataptr_auto_cast<>::data_ptr
    virtual char* const data_ptr() const override
    {
        return m_pData.get();
    }
};

int main()
{
    auto a = union_array<int, char, float, double>(5); //Create a new union_array object with enough space to store either 5 int, 5 char, 5 float or 5 double values.

    ((int*)a)[3] = 3; //Use as int array
    auto b = a; //copy
    ((int*)b)[3] = 1; //Change a value

    auto c = std::move(a);// move a to c, a is invalid beyond this point

//  std::cout << ((int*)a)[3] << std::endl; //This will crash as a is invalid due to the move
    std::cout << ((int*)b)[3] << std::endl; //prints "1"
    std::cout << ((int*)c)[3] << std::endl; //prints "3"
}

<强>解释

template <typename T, typename...Args>
struct max_size_of
{
    static const std::size_t value = std::max(sizeof(T), max_size_of<Args...>::value);
};

template <typename T>
struct max_size_of<T>
{
    static const std::size_t value = sizeof(T);
};

max_size_of<>用于获取作为模板参数传递的所有类型的最大sizeof()值。

让我们先看一下这个简单的案例。    - max_size_of<char>::valuevalue将设置为sizeof(char)。    - max_size_of<int>::valuevalue将设置为sizeof(int)。    - 等等

如果你输入多种类型,它将评估这些类型的sizeof的最大值。

对于2种类型,它看起来像这样:max_size_of<char, int>::valuevalue将设置为std::max(sizeof(char), max_size_of<int>::value)。 如上所述,max_size_of<int>::valuesizeof(int)相同,因此max_size_of<char, int>::valuestd::max(sizeof(char), sizeof(int))相同,与sizeof(int)相同。

template <typename T, typename...Args>
struct dataptr_auto_cast : public dataptr_auto_cast<Args...>
{
    virtual char* const data_ptr() const = 0;
    explicit operator T* const() const { return (T*)data_ptr(); }
};

template <typename T>
struct dataptr_auto_cast<T>
{
    virtual char* const data_ptr() const = 0;
    explicit operator T* const() const { return (T*)data_ptr(); }
};

dataptr_auto_cast<>是我们用作简单的抽象基类的。 它迫使我们在最后一个类中实现一个函数char* const data_ptr() const(它将是union_array)。

让我们假设该类不是抽象的,并使用简单版本dataptr_auto_cast<T>: 该类实现一个操作符函数,该函数返回传递的模板参数类型的指针。 dataptr_auto_cast<int>有一个函数explicit operator int* const() const;

该函数通过data_ptr()函数提供对派生类提供的数据的访问,并将其强制转换为T* const类型。 const是指针不会被意外更改,explicit关键字用于避免不必要的隐式转换。

正如您所看到的,dataptr_auto_cast<>有2个版本。一个有1个模板参数(我们刚看过)和一个有多个模板参数。 该定义非常相似,只有多个参数一个继承dataptr_auto_cast,其中一个(第一个)模板参数较少。

因此dataptr_auto_cast<int, char>有一个函数explicit operator int* const() const;并继承了dataptr_auto_cast<char>,其函数为explicit operator char* const() const;。 如您所见,您传递的每种类型都实现了一个强制转换操作符函数。

只有一个例外,即两次传递相同的模板参数。 这将导致相同的操作符函数在相同的类中被定义两次,这不起作用。 对于这个用例,使用它作为union_array的基类,这应该不重要。

现在这两个很清楚,让我们看一下union_array的实际代码:

template <typename...Args>
struct union_array : public dataptr_auto_cast<Args...>
{
    static const size_t blockSize = max_size_of<Args...>::value;

private:
    std::unique_ptr<char[]> m_pData;
    size_t m_size;

public:

    //Create a new array to store "size" elements
    union_array(size_t size)
        :m_pData(new char[size*blockSize])
        ,m_size(size)
    {
    }

    //Copy constructor
    union_array(const union_array<Args...>& other)
        :m_pData(new char[other.m_size*blockSize])
        ,m_size(other.m_size)
    {
        memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
    }

    //Move constructor
    union_array(union_array<Args...>&& other)
        :m_pData(std::move(other.m_pData))
        ,m_size(std::move(other.m_size))
    {
    }


    union_array& operator=(const union_array<Args...>& other)
    {
        m_pData = new char[other.m_size*blockSize];
        m_size = other.m_size;
        memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
    }

    union_array& operator=(union_array<Args...>&& other)
    {
        m_pData = std::move(other.m_pData);
        m_size = std::move(other.m_size);
    }

    ~union_array() = default;

    size_t size() const
    {
        return m_size;
    }

    virtual char* const data_ptr() const override
    {
        return m_pData.get();
    }
};

正如您所见,union_array<>使用相同的模板参数从dataptr_auto_cast<>继承。 因此,这为我们提供了作为模板参数传递给union_array<>的每种类型的强制转换运算符。

同样在union_array<>的末尾,您可以看到char* const data_ptr() const函数已实现(来自dataptr_auto_cast<>的抽象函数)。

下一个有趣的事情是static const size_t blockSize,它使用模板参数的最大sizeof值初始化为union_array<>。 要获得此值,请使用max_size_of,如上所述。

该类使用std::unique_ptr<char[]>作为数据存储,因为std::unique_ptr会在类被销毁后自动为我们删除空间。 同样std::unique_ptr能够移动语义,它在移动赋值操作符函数和移动构造函数中使用。

还包括“普通”复制分配操作符函数和复制构造函数,并相应地复制内存。

该类有一个构造函数union_array(size_t size),它接受​​union_array应该能够容纳的元素数。 将此值与blockSize相乘可为我们提供存储最大模板类型的size个元素所需的空间。

最后但并非最不重要的是,如果需要,有一种访问方法可以要求size()

答案 2 :(得分:1)

C ++要求在编译时知道类型的大小。

无需知道数据块的大小,但所有类型都有已知的大小。

围绕它有三种方法。

我暂时忽略了工会部分。想象一下,如果你想:

struct some (int how_many) {
  int data[how_many];
};

因为联合部分增加了可以单独处理的复杂性。

首先,您可以将指针/引用/等存储到数据中,而不是将数据存储为类型的一部分。

struct some {
  std::vector<int> data;

  explicit some( size_t how_many ):data(how_many) {};

  some( some&& ) = default;
  some& operator=( some&& ) = default;
  some( some const& ) = default;
  some& operator=( some const& ) = default;
  some() = default;
  ~some() = default;
};

这里我们将数据存储在std::vector - 动态数组中。我们默认复制/移动/构造/破坏操作(显式 - 因为它使它更清晰),并且正确的事情发生。

我们可以使用unique_ptr代替向量:

struct some {
  std::unique_ptr<int[]> data;

  explicit some( size_t how_many ):data(new int[how_many]) {};

  some( some&& ) = default;
  some& operator=( some&& ) = default;
  some() = default;
  ~some() = default;
};

这会阻止对结构的复制,但在典型的std实现中,结构从3个指针的大小变为1的大小。我们失去了在事后轻松调整大小的能力,并且无需自己编写代码即可复制。

下一个方法是模板化。

template<std::size_t N>
struct some {
  int data[N];
};
但是,这需要在编译时知道结构的大小,some<2>some<3>是不相关的类型&#39; (禁止模板模式匹配)。所以它有缺点。

最后一种方法是类似C的。在这里,我们依赖于数据大小可变的事实,即使类型不是。

struct some {
  int data[1]; // or `0` in some compilers as an extension
};

some* make_some( std::size_t n ) {
  Assert(n >= 1); // unless we did `data[0]` above
  char* buff = new some[(n-1)*sizeof(int) + sizeof(some)]; // note: alignment issues on some platforms?
  return new(buff) some(); // placement new
};

我们为可变大小的some分配缓冲区。通过data[13]访问缓冲区实际上是合法的,实际上可能也是如此。

这种技术在C中用于创建可变大小的结构。

对于union部分,您将要创建一个char的缓冲区,其大小为std::max(sizeof(float), sizeof(int))*N,并公开函数:

char* data(); // returns a pointer to the start of the buffer
int* i() { return reinterpret_cast<int*>(data()); }
float* f() { return reinterpret_cast<float*>(data()); }

您可能还需要将数据正确初始化为正确的类型;从理论上讲,char s的'\0'缓冲区可能与定义的float值或int s不对应。

答案 3 :(得分:1)

我想建议一种不同的方法:不要将元素数量与union联系起来,而是将其绑定在外面:

union Some
{
  int i;
  float f;
};

Some *get_Some(int le) { return new Some[le]; }

不要忘记delete[] get_Some的返回值...或者使用智能指针:

std::unique_ptr<Some[]> get_Some(int le)
{ return std::make_unique<Some[]>(le); }

您甚至可以创建Some_Manager

struct Some_Manager
{
    union Some
    {
      int i;
      float f;
    };

    Some_Manager(int le) :
        m_le{le},
        m_some{std::make_unique<Some[]>(le)}
    {}

    // ... getters and setters...
    int count() const { return m_le; }
    Some &operator[](int le) { return m_some[le]; }

private:
    int m_le{};
    std::unique_ptr<Some[]> m_some;
};

查看 Live example

答案 4 :(得分:0)

无法声明具有动态大小的结构,正如您尝试的那样,必须在运行时指定大小,否则您将不得不使用更高级别的抽象来管理动态池在运行时的内存。

此外,在您的第二个示例中,您在联合中包含le。如果您尝试执行的操作是可能的,则会导致leif的第一个值重叠。

如前所述,如果在编译时已知大小,则可以使用模板执行此操作:

#include <cstdlib>

template<size_t Sz>
union U {
    int i[Sz];
    float f[Sz];
};

int main() {
    U<30> u;
    u.i[0] = 0;
    u.f[1] = 1.0;
}

http://ideone.com/gG9egD

如果你想要动态尺寸,你就会开始进入最好使用像std::vector这样的东西。

#include <vector>
#include <iostream>

union U {
    int i;
    float f;
};

int main() {
    std::vector<U> vec;

    vec.resize(32);

    vec[0].i = 0;
    vec[1].f = 42.0;

    // But there is no way to tell whether a given element is
    // supposed to be an int or a float:
    // vec[1] was populated via the 'f' option of the union:
    std::cout << "vec[1].i = " << vec[1].i << '\n';
}

http://ideone.com/gjTCuZ