哪种类型特征表明该类型是 memcpy 可分配的? (元组、对)

2024-01-02

我想知道我可以执行什么类型自省来检测可通过简单的原始内存复制分配的类型?

例如,据我了解,内置类型、内置类型的元组和此类元组的元组都属于此类。 动机是如果可能的话我想传输原始字节。

T t1(...); // not necessarely default constructible 
T t2(...);

t1 = t2; // should be equivalent to std::memcpy(&t1, &t2, sizeof(T));
// t1 is now an (independent) copy of the value of t2, for example each can go out of scope independently

What type_trait or 组合 of type_traits可以在编译时判断赋值是否可以(原则上)替换为memcpy?

我尝试了对我认为应该满足此条件的类型有效的方法,但令我惊讶的是,唯一适合该行为的类型不是std::is_trivially_assignable but std::trivially_destructible。 这在某种程度上是有道理的,但我很困惑为什么其他一些选项不适用于预期的情况。

我知道可能没有一种防弹方法,因为人们总是可以编写一个有效地可内存复制的类,该类不能被“检测”为可内存复制,但我正在寻找一种适用于简单直观情况的方法。

#include<type_traits>
template<class T> using trait = 
    std::is_trivially_destructible
//  std::is_trivial
//  std::is_trivially_copy_assignable
//  std::is_trivially_copyable // // std::tuple<double, double> is not trivially copyable!!!
//  std::is_trivially_default_constructible
//  std::is_trivially_default_constructible
//  std::is_trivially_constructible
//  std::is_pod // std::tuple<double, double> is not pod!!!
//  std::is_standard_layout
//  std::is_aggregate
//  std::has_unique_object_representations
    <T>
;

int main(){
    static_assert((trait<double>{}), "");
    static_assert((trait<std::tuple<double, double>>{}), "");
    static_assert((not trait<std::tuple<double, std::vector<double>>>{}), "");
    static_assert((not trait<std::vector<double>>{}), "");
}

当然,我认为元组应该是可复制的并不是基于标准,而是基于常识和实践。也就是说,因为这通常是可以的:

std::tuple<double, std::tuple<char, int> > t1 = {5.1, {'c', 8}};
std::tuple<double, std::tuple<char, int> > t2;
t2 = t1;
std::tuple<double, std::tuple<char, int> > t3;
std::memcpy(&t3, &t1, sizeof(t1));
assert(t3 == t2);

As a proof of principle, I implemented this. I added a couple of conditions related to the size to avoid some possible misleading specialization of std::tuple.

template<class T> 
struct is_memcopyable 
: std::integral_constant<bool, std::is_trivially_copyable<T>{}>{};

template<class T, class... Ts> 
struct is_memcopyable<std::tuple<T, Ts...>> : 
    std::integral_constant<bool, 
        is_memcopyable<T>{} and is_memcopyable<std::tuple<Ts...>>{}
    >
{};

template<class T1, class T2> 
struct is_memcopyable<std::pair<T1, T2>> : 
    std::integral_constant<bool, 
        is_memcopyable<T1>{} and is_memcopyable<T2>{}
    >
{};

这是一个非常有限的解决方法,因为像这样的类:

struct A{ std::tuple<double, double> t; }; 

不幸的是,仍然会被报告为不可平凡复制和不可内存复制。


正确的测试其实是std::is_trivially_copyable,这允许使用memcpy用于创建新对象和修改现有对象。

尽管您可能会感到惊讶,对于您的直觉告诉您的类型,这些返回 falsememcpy应该没问题,他们没有说谎;该标准确实使memcpy在这些情况下未定义的行为。


在特定情况下std::pair,我们可以深入了解哪里出了问题:

int main()
{
    typedef std::pair<double,double> P;
    std::cout << "\nTC:  " << std::is_trivially_copyable<P>::value;
    std::cout << "\nTCC: " << std::is_trivially_copy_constructible<P>::value;
    std::cout << "\nTCv: " << std::is_trivially_constructible<P, const P&>::value;
    std::cout << "\n CC: " << std::is_copy_constructible<P>::value;
    std::cout << "\n MC: " << std::is_move_constructible<P>::value;
    std::cout << "\nTCA: " << std::is_trivially_copy_assignable<P>::value;
    std::cout << "\nTCvA:" << std::is_trivially_assignable<P, const P&>::value;
    std::cout << "\n CA: " << std::is_copy_assignable<P>::value;
    std::cout << "\n MA: " << std::is_move_assignable<P>::value;
    std::cout << "\nTD:  " << std::is_trivially_destructible<P>::value;
}

TC:0 TCC:1 TCv:1 抄送:1 主持人:1 三氯乙酸:0 TCvA:0 加州:1 硕士:1 达阵:1

Evidently it isn't trivially copy assignable.1

The pair赋值运算符是用户定义的,因此并不简单。


1I think that clang, gcc, and msvc are all wrong here, actually, but if it satisfied std::_is_trivially_copy_assignable it wouldn't help, because TriviallyCopyable requires that the copy constructor, if not deleted, is trivial, and not the TriviallyCopyAssignable trait. Yeah, they're different.

X 类的复制/移动赋值运算符很简单如果不是用户提供的 and...

vs

is_assignable_v<T, const T&>为 true 并且赋值,如定义所示is_assignable,众所周知,不会调用任何不平凡的操作。

调用的操作pair<double, double>的复制赋值运算符是各个双打的赋值,其中are琐碎的。

不幸的是,定义可以简单地复制依赖于第一个,即pair fails.

一个普通可复制的类是一个类:

  • 其中每个复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符要么被删除,要么变得微不足道,
  • 至少有一个未删除的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符,并且
  • 它有一个简单的、未删除的析构函数。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

哪种类型特征表明该类型是 memcpy 可分配的? (元组、对) 的相关文章

随机推荐