std :: pair <u,v>对齐控件</u,v>

时间:2013-08-16 03:59:26

标签: c++ memory-alignment std-pair

我注意到std :: pair在尝试将其保存到二进制文件时会发生一件令人不快的事情:std :: pair与一个单词对齐。它在处理器效率方面可能很有用,但需要更多的存储空间,所以我想将对齐模式切换为1字节的std :: pair。我的编译器是MS VC ++ 2012。

#include <iostream>

int main( )
{
    struct S_a { double a; size_t b; };

#pragma pack(1)
    struct S_wa { double a; size_t b; };

    std::cout << sizeof( size_t ) << '\n';                          // 4
    std::cout << sizeof( double ) << '\n';                          // 8
    std::cout << sizeof( std::pair< size_t, size_t > ) << '\n';     // 8
    std::cout << sizeof( std::pair< double, size_t > ) << '\n';     // 16 - bad
    std::cout << sizeof( S_wa ) << '\n';                            // 12 - good
    std::cout << sizeof( S_a ) << '\n';                             // 16
    std::cout << sizeof( std::pair< double, double > ) << '\n';     // 16
}

我试过这个,但它不起作用:

#pragma pack(1)
    typedef std::pair< double, size_t > Q;

    std::cout << sizeof( Q ) << '\n';                               // 16

3 个答案:

答案 0 :(得分:6)

std::pair基本上减少到:

class xxx
{
   T1 _t1;
   T2 _t2;
};

两个成员的对齐由定义模板本身时的强制对齐定义,而不仅仅是在用于实例化实例时。

STL的Microsoft实现使用符号_CRT_PACKING来定义用于所有STL组件的打包。默认情况下,打包设置为8.如果在包含定义std::pair<utility>)的标题之前自己定义此符号,理论上可以覆盖打包并设置自己的标记。

请注意调用库或其他采用标准打包的代码时可能遇到的潜在问题。

答案 1 :(得分:4)

抱歉,pack编译指示在这种情况下不适合您。你可以

#define _CRT_PACKING 1
#include <utility>

这可能会导致所有问题。其中之一是某些API,特别是低级API,期望数据以某种方式对齐。并非所有人都足够聪明,能够处理不是这样的情况;这可能导致毫不客气的崩溃。

如果你真的想要像这样序列化对象,请自己处理std::pair<U,V>的序列化(仅限示例):

template<typename U, typename V>
void paircpy(char *dest, const std::pair<U, V> &pair) {
    memcpy(buffer, &pair.first, sizeof(U));
    memcpy(buffer + sizeof(U), &pair.second, sizeof(V));
}

您可能希望处理非memcpy的数据类型的特殊情况。

对于任何严肃的项目,你真正应该做的是以可移植的方式进行序列化对象,以便它们可以检索并优雅地处理诸如浮点,签名数据类型的不同编码,指针之类的东西,数组,STL容器以及其他任何不可能或者不足以简单地转储对象内存的东西。

阅读C++ FAQ并开发自己的序列化模块,这些模块不仅仅是将对象的内存中表示转储到文件中。

或者,您可以使用预先打包的便携式解决方案来序列化数据类型,例如

答案 2 :(得分:0)

好吧,完全不同的'解决方案'可能是也可能不是一个好主意,但是嘿,如果它有效,我是谁阻止你知道。它提供了与其他答案相同的警告,但允许您挑选和选择毒药。

对您所关注的类型进行std::pair的部分特化,使用该特定定义的pragma包,例如。对于charint

namespace std {
#pragma pack( /* ... whatever you want ... */ )
template<> struct pair<char,int> {
  char first;
  int second;
};
}

根据定义你可以触摸它,你可以影响包装。同样,如果你愿意为所有相关类型做这件事,这只是好事,并记住其他海报提到的警告。

并且您使用的库很少有机会疯狂并且决定在pair中添加更多其他lib依赖的东西,但您可能不需要担心关于它