c++ - 在连续内存中存储任意元素

标签 c++ c++11 templates variadic-templates memory-alignment

我正在尝试创建一个数据结构,它将在连续内存中保存 N 个不同类型。所以在编译时我可以说我想存储 3 种不同类型的 4 个元素,在内存中它看起来像 111122223333。

我一直在使用可变参数模板方法,我认为它可以满足我的要求,但是我不确定如何在 add 方法中将元素添加到每个数组中。

template<std::size_t N, typename... Args>
class Batch
{
    private:
        std::tuple<std::array<Args, N>...> data_;
        size_t currentPos_;

    public:
        template<typename T>
        void addToArray(std::array<T, N>& array, const T& value)
        {
            array[currentPos_] = value;
        }

        void add(const Args&... values)
        {
            //????
            addToArray(/*array, value*/);

            currentPos_++;
        }

        const void* data()
        {
            &return data_;
        }
};


int main()
{
    Batched<3, float, double, int> b;

    b.add(1.0f, 1.0, 1);
    b.add(2.0f, 2.0, 2);
    b.add(3.0f, 3.0, 3);
    b.add(4.0f, 4.0, 4);
    return 0;
}

即使我让它工作,内存布局是否正确?有更好的方法吗?

最佳答案

我认为这不是一个好主意,但是......我展示它只是为了好玩

使用 std::vector<char> (以及由 C++11 添加的方法 data() 授予的对以下内存的访问权限)和旧的 memcpy() ,我想你可以简单地做如下

#include <vector>
#include <cstring>
#include <iostream>

template <typename... Args>
class Batch
 {
   private:
      std::vector<char> buffer;

   public:

      void addHelper ()
       { }

      template <typename T, typename ... Ts>
      void addHelper (T const & v0, Ts ... vs)
       { 
         auto  pos = buffer.size();

         buffer.resize(pos + sizeof(T));

         std::memcpy(buffer.data() + pos, & v0, sizeof(T));

         addHelper(vs...);
       }

      void add (const Args&... values)
       { addHelper(values...); }

      const void * data()
       { return buffer.data(); }

      void toCout ()
       { toCoutHelper<Args...>(0U, buffer.size()); }

      template <typename T, typename ... Ts>
      typename std::enable_if<(0U < sizeof...(Ts)), void>::type
         toCoutHelper (std::size_t  pos, std::size_t  size)
       {
         if ( pos < size )
          {
            T val;

            std::memcpy( & val, buffer.data() + pos, sizeof(T) );

            std::cout << " - " << val << std::endl;

            toCoutHelper<Ts...>(pos+sizeof(T), size);
          }
       }

      template <typename T, typename ... Ts>
      typename std::enable_if<0U == sizeof...(Ts), void>::type
         toCoutHelper (std::size_t  pos, std::size_t  size)
       {
         if ( pos < size )
          {
            T val;

            std::memcpy( & val, buffer.data() + pos, sizeof(T) );

            std::cout << " - " << val << std::endl;

            toCoutHelper<Args...>(pos+sizeof(T), size);
          }
       }

 };


int main()
 {
   Batch<float, double, int> b;

   b.add(1.0f, 1.0, 1);
   b.add(2.0f, 2.0, 2);
   b.add(3.0f, 3.0, 3);
   b.add(4.0f, 4.0, 4);

   b.toCout();

   return 0;
 }

--- 编辑 ---:添加了一个方法,toCout()打印(到 std::cout )所有存储的值;只是建议如何使用这些值。

--- 编辑 2 ---:正如 ildjarn 所指出的(谢谢!)如果在 Args... 中,这个解决方案是非常危险的。类型是一些非 POD(普通旧数据)类型。

this page 中对此进行了很好的解释.

我转录相关部分

An example of a type that cannot be safely copied using memcpy is std::string. This is typically implemented using a reference-counted shared pointer, in which case it will have a copy constructor that causes the counter to be incremented. If a copy were made using memcpy then the copy constructor would not be called and the counter would be left with a value one lower than it should be. This would be likely to result in premature deallocation of the memory block that contains the character data.

--- 编辑 3 ---

正如 ildjarn 所指出的(再次感谢!),使用此解决方案离开 data() 是非常危险的。成员(member)。

如果有人使用这种方式返回的指针

   char const * pv = (char const *)b.data();

   size_t  pos = { /* some value here */ };

   float  f { *(float*)(pv+pos) };  // <-- risk of unaligned access

在某些架构中,可能会导致访问 float *在可能杀死程序的未对齐地址中

data() 返回的指针恢复值的正确(且安全)方法是 toCoutHelper() 中使用的那个,使用`std::memcpy()

   char const * pv = (char const *)b.data();

   size_t  pos = { /* some value here */ };

   float  f; 

   std::memcpy( & f, pv + pos, sizeof(f) );

关于c++ - 在连续内存中存储任意元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39821244/

相关文章:

使用预定义参数引用 C++ 函数

c++ - 使用 memcpy 尝试将一个结构复制到 char[] 缓冲区中

C++ 运算符重载 - 我实际上返回什么操作数类型?

javascript - ListView 与 NumericTextBox 数据初始化

c++ - 如果从未调用过,类模板的成员函数是否不会被实例化?

c++ - Mersenne Twister 跨编译器的可重复性

c++ - std::map 的反向迭代器上的 "operator-"不匹配

c++ - std::function 无法区分重载函数

c++ - std::initializer_list 的语义

javascript - 生态模板: Combine multiple templates from the command-line?