重载operator == for pods

blu*_*gon 9 c++ memory templates memcmp template-meta-programming

我正在研究一些具有高级接口的低级代码,并且需要比较运算符来进行普通旧数据类型(如FILETIME struct)的单元测试,但由于C++甚至不提供成员比较,所以我写了这样的:

template <typename Type>
std::enable_if_t<std::is_pod<Type>::value, bool> operator==(const Type& a,
                                                            const Type& b) {
  return std::memcmp(&a, &b, sizeof(Type)) == 0;
}
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,这是一个好方法还是有一些隐藏的恶魔会让我在开发周期后期遇到麻烦,但它现在有点工作.

Mic*_*ler 0

这个问题是定义通用比较运算符的受限变体,如评论中所述。填充对 POD 提议的危险和影响的一个示例operator==是:

template <typename Type>
std::enable_if_t<std::is_pod<Type>::value, bool> operator==(const Type& a,
                                                            const Type& b) 
{
  return std::memcmp(&a, &b, sizeof(Type)) == 0;
}

struct St {
    bool a_bool;
    int an_int;
};
union Un {
    char buff[sizeof(St)];
    St st;
};

std::ostream &operator<<(std::ostream & out, const St& data)
{
   return out << '{' << std::boolalpha << data.a_bool << ", " << data.an_int << '}';
}

int main()
{
    Un un{{1,2,3,4,5}};
    new (&un.st) St;
    un.st.a_bool = true;
    un.st.an_int = 5;

    St x={true, 5};
    std::cout << "un.a=" << un.st << '\n';
    std::cout << "x=" << x << '\n';
    std::cout << (x == un.st) << "\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

两者un.stx包含相同的数据,但un.st在填充字节中包含一些垃圾。填充的垃圾使得提议operator==返回false逻辑上等效的对象。以下是 gcc (head-9.0.0) 和 clang (head-8.0.0) 的输出:

un.a={true, 5}
x={true, 5}
false
Run Code Online (Sandbox Code Playgroud)

更新:这种情况也会发生在常规的 new/delete 中,如在 wandbox.org 上运行的那样:

std::enable_if_t<std::is_pod<Type>::value, bool> operator==(const Type& a,
                                                            const Type& b) 
{
  return std::memcmp(&a, &b, sizeof(Type)) == 0;
}

struct St {
    bool a_bool;
    int an_int;
};

std::ostream &operator<<(std::ostream & out, const St& data)
{
   return out << '{' << std::boolalpha << data.a_bool << ", " << data.an_int << '}';
}

static constexpr unsigned N_ELEMENTS = 2;
int main()
{
    {
        volatile char * arr = new char[sizeof(St) * N_ELEMENTS];
        for (unsigned i=0; i < sizeof(St) * N_ELEMENTS ; ++i)
            arr[i] = i + 1;
        std::cout << "arr = " << (void*)arr << "\n";
        delete[] arr;
    }

    St * ptr_st = new St[N_ELEMENTS];
    std::cout << "ptr_st = " << ptr_st << "\n";
    for (unsigned i=0 ; i != N_ELEMENTS; ++i) {
       ptr_st[i].a_bool = true;
       ptr_st[i].an_int = 5;
    }
    St x={true, 5};
    std::cout << "x=" << x << '\n';
    std::cout << "ptr_st[1]=" << ptr_st[1] << '\n';
    std::cout << (x == ptr_st[1]) << "\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

其输出为:

arr = 0x196dda0
ptr_st = 0x196dda0
x={true, 5}
ptr_st[1]={true, 5}
false
Run Code Online (Sandbox Code Playgroud)