在 C++ 中创建模板类的打包版本 14

Creating a packed version of a template class in C++14

提问人:azyo 提问时间:9/8/2023 最后编辑:azyo 更新时间:9/11/2023 访问量:52

问:

我想制作一个模板类的版本,在打包的结构中使用它以作为字节发送或接收。但我不希望打包的成本影响算术和其他方面对类的常规使用(使用 GCC 和 c++14 为 ARM 编译)。__attribute__((packed))

所以我有一个模板类,如这个例子:

template<typename T>
struct Vec3 {
    T x, y, z;

    constexpr Vec3() = default;
    constexpr Vec3(T x, T y, T z) : x(x), y(y), z(z) {}

    constexpr Vec3& operator=(const Vec3& v);

    constexpr Vec3 operator+(const Vec3& v) const;

    template<typename U>
    constexpr auto operator+(const Vec3<U>& v) const -> Vec3<decltype(T{} + U{})>;

    // many other member functions
}

template<typename T, typename U>
constexpr auto dot(const Vec3<T>& v1, const Vec3<U>& v2) -> decltype(T{} * U{});

// other non-member functions

现在我想做一个 Vec3 类的打包版本,如下所示:

#include <type_traits>

template<typename T>
class PackedType;

template<typename T>
using Packed = std::conditional_t<std::is_arithmetic<T>::value, T, PackedType<T>>;

template<typename T>
struct __attribute__((packed)) PackedType<Vec3<T>> {
    Packed<T> x, y, z; // make sure the internal type is packable

    constexpr PackedType(const Vec3<T>& v) : x(v.x), y(v.y), z(v.z) {}

    constexpr operator Vec3<T>() const { return Vec3<T>(T(x), T(y), T(z)); }
}

但这就是它崩溃的地方。对函数的调用期望使用模板参数推导/替换,但不是 ,也不是派生自 。所以编译器无法推断出任何东西。Vec3PackedType<Vec3<T>>Vec3Vec3

我们希望在函数中使用时将打包类转换为常规类。正如我之前所说,我们不希望/不需要对打包的值执行操作,因此制作副本是可以的。Vec3Vec3

但是现在我们需要为所有函数提供重载以使用类的打包版本,以及所有(常规,打包),(打包,常规),(打包,打包)函数的两个参数等等......我指的不仅仅是 Vec3.h/cpp 文件,任何作为模板化为参数的函数都可能需要重载。Vec3<T>

至于其他选项:

  • 打包的类不能派生,因为常规没有打包(我的 GCC 版本不会在该版本上引发错误,即使它可能是未定义的行为)。Vec3Vec3Vec3
  • 对包装和非包装进行专业化并不能解决必须复制所有内容的问题。template<typename T, bool Packed> struct Vec3 {}
  • 每次我们需要进行转换时都显式使用 a(如果您忘记了,请在编译器输出时哭泣)v.unpack()

需要明确的是,我想要的是一种自动将打包类转换为常规类的方法,例如,就像 c 数组衰减为指针一样。

C++ 模板 C++14 打包

评论

0赞 463035818_is_not_an_ai 9/8/2023
吹毛求疵:它不是一个模板类,而是一个类模板。类模板不是类,而是模板。
0赞 463035818_is_not_an_ai 9/8/2023
“对期望 Vec3 的函数的调用使用模板参数推导/替换...”有什么功能?问题在于那些函数中期望 a 而您实际上希望它们接受任何可以转换为 .你能修改这些功能吗?Vec3Vec3
0赞 463035818_is_not_an_ai 9/8/2023
...或者,也许您希望函数接受任何具有 和 成员的内容?xyz
0赞 463035818_is_not_an_ai 9/8/2023
哦,对不起,已经包含了一个示例:dot
1赞 463035818_is_not_an_ai 9/8/2023
我的观点是写一个,那么就没有“不同的类型”,decution 总是有效的。然后你可以通过 SFINAE 添加约束,例如“T 和 U 必须转换为 ,然后你可以将 whatever 和 is 转换为 s。我实际上正要写一个答案,但我错过了 c++14 标签,结果一团糟dot(const T& t, const U& u)Vec<T>tuVec<T>

答:

0赞 azyo 9/8/2023 #1

评论中建议的答案可以解决问题。

#include <type_traits>

template<typename T>
struct Vec3 {
    using type = T;
    T x, y, z;
    //...
}

template<typename T>
struct isVec3Conv : std::false_type {};

template<typename T>
struct isVec3Conv<Vec3<T>> : std::true_type {};

template<typename T>
struct isVec3Conv<PackedType<Vec3<T>>> : std::true_type {};


template<typename V1, typename V2,
    std::enable_if_t<isVec3Conv<V1>::value && isVec3Conv<V2>::value, bool> = 0>
constexpr auto dot(const V1& v1, const V2& v2) ->
    decltype(typename V1::type{} * typename V2::type{});

在这种情况下,我们可以替换为 ,但我的类有更多的模板类型参数,因此我的实现变得更长一些。typename V1::type{}v1.x