当前位置: 首页 > 知识库问答 >
问题:

[[no_unique_address]]和同一类型的两个成员值

黄丰
2023-03-14

我在玩c20中的[[no_unique_address]]

在cppreference的例子中,我们有一个空类型< code>Empty和类型< code>Z

struct Empty {}; // empty class

struct Z {
    char c;
    [[no_unique_address]] Empty e1, e2;
};

显然,< code>Z的大小必须至少为< code>2,因为< code>e1和< code>e2的类型相同。

但是,我真的希望< code>Z的大小为< code>1。这让我想到,如何用额外的模板参数将< code>Empty包装在某个包装类中,以强制执行不同类型的< code>e1和< code>e2。

template <typename T, int i>
struct Wrapper : public T{};

struct Z1 {
    char c;
    [[no_unique_address]] Wrapper<Empty,1> e1;
    [[no_unique_address]] Wrapper<Empty,2> e2;
};

不幸的是,大小(Z1)==2。有没有一个技巧可以使Z1的大小成为一个?

我正在使用gcc版本9.2.1clang版本9.0.0进行测试。

在我的应用程序中,我有很多空类型的表单。

template <typename T, typename S>
struct Empty{
    [[no_unique_address]] T t;
    [[no_unique_address]] S s;
};

如果TS也是空类型并且不同,则哪个是空类型!即使TS是相同的类型,我也希望此类型为空。

共有2个答案

梁泰
2023-03-14

据我所知,如果您想拥有两个成员,这是不可能的。但是当类型相同且为空时,您可以专门化并且只有一个成员:

template <typename T, typename S, typename = void>
struct Empty{
    [[no_unique_address]] T t;
    [[no_unique_address]] S s;

    constexpr T& get_t() noexcept { return t; };
    constexpr S& get_s() noexcept { return s; };
};

template<typename TS>
struct Empty<TS, TS, typename std::enable_if_t<std::is_empty_v<TS>>>{
    [[no_unique_address]] TS ts;

    constexpr TS& get_t() noexcept { return ts; };
    constexpr TS& get_s() noexcept { return ts; };
};

当然,使用成员的程序的其余部分需要更改,以处理只有一个成员的情况。在这种情况下使用哪个成员并不重要 - 毕竟,它是一个没有唯一地址的无状态对象。显示的成员函数应该使这变得简单。

不幸的是,sizeof(空

您可以引入更多专项来支持空对的递归压缩:

template<class TS>
struct Empty<Empty<TS, TS>, TS, typename std::enable_if_t<std::is_empty_v<TS>>>{
    [[no_unique_address]] Empty<TS, TS> ts;

    constexpr Empty<TS, TS>& get_t() noexcept { return ts; };
    constexpr TS&            get_s() noexcept { return ts.get_s(); };
};

template<class TS>
struct Empty<TS, Empty<TS, TS>, typename std::enable_if_t<std::is_empty_v<TS>>>{
    [[no_unique_address]] Empty<TS, TS> ts;

    constexpr TS&            get_t() noexcept { return ts.get_t(); };
    constexpr Empty<TS, TS>& get_s() noexcept { return ts; };
};

更重要的是,要压缩Empty之类的内容

template <typename T, typename S>
struct Empty<Empty<T, S>, S, typename std::enable_if_t<std::is_empty_v<S>>>{
     [[no_unique_address]] Empty<T, S> ts;

    constexpr Empty<T, S>& get_t() noexcept { return ts; };
    constexpr S&           get_s() noexcept { return ts.get_s(); };
};

template <typename T, typename S>
struct Empty<Empty<S, T>, S, typename std::enable_if_t<std::is_empty_v<S>>>{
     [[no_unique_address]] Empty<S, T> st;

    constexpr Empty<S, T>& get_t() noexcept { return st; };
    constexpr S&           get_s() noexcept { return st.get_t(); };
};


template <typename T, typename S>
struct Empty<T, Empty<T, S>, typename std::enable_if_t<std::is_empty_v<T>>>{
     [[no_unique_address]] Empty<T, S> ts;

    constexpr T&           get_t() noexcept { return ts.get_t(); };
    constexpr Empty<T, S>  get_s() noexcept { return ts; };
};

template <typename T, typename S>
struct Empty<T, Empty<S, T>, typename std::enable_if_t<std::is_empty_v<T>>>{
     [[no_unique_address]] Empty<S, T> st;

    constexpr T&           get_t() noexcept { return st.get_s(); };
    constexpr Empty<S, T>  get_s() noexcept { return st; };
};

时宾实
2023-03-14

如果TS也是空类型并且不同,则哪个是空类型!即使TS是相同的类型,我也希望此类型为空。

你不能得到那个。从技术上讲,即使< code>T和< code>S是不同的空类型,你甚至不能保证它一定是空的。记住:< code>no_unique_address是一个属性;它隐藏对象的能力完全依赖于实现。从标准的角度来看,您不能强制空对象的大小。

随着C 20实现的成熟,您应该假设[[no_unique_address]通常会遵循空基优化的规则。也就是说,只要两个相同类型的对象不是子对象,您就可以期待隐藏。但在这一点上,这有点运气。

至于TS是同一类型的具体情况,这根本不可能。尽管名称“no_unique_address”的含义,但现实情况是,C要求在给定两个指向相同类型对象的指针时,这些指针要么指向同一对象,要么具有不同的地址。我称之为“唯一身份规则”,no_unique_address不会影响这一点。从 [介绍对象]/9:

如果一个对象嵌套在另一个对象中,或者如果至少一个对象是大小为零的子对象,并且它们的类型不同,那么具有重叠生存期的两个对象(不是位字段)可能具有相同的地址;否则,它们具有不同的地址并占用不相交的存储字节。

声明为 [[no_unique_address]] 的空类型的成员大小为零,但具有相同的类型使得这是不可能的。

实际上,仔细想想,试图通过嵌套隐藏空类型仍然违反了唯一标识规则。考虑您的< code >包装器和< code>Z1案例。给定一个< code>z1,它是< code>Z1的实例,显然< code>z1.e1和< code>z1.e2是不同类型的不同对象。但是,< code>z1.e1不会嵌套在< code>z1.e2中,反之亦然。虽然它们有不同的类型,< code >(空

根据唯一标识规则,它们必须具有不同的地址。因此,即使 e1e2 在名义上是不同的类型,它们的内部也必须服从对同一包含对象中的其他子对象的唯一标识。递 归。

不管你怎么努力,你想要的在C语言中是不可能的。

 类似资料:
  • 问题内容: 我有两个成员相同,我想将一个结构复制到另一个结构,请参见下面的伪代码: 然后,我有结构的,而结构的,有什么办法复制的? 问题答案: 使用转换更改类型。以下代码使用转换将type 的值复制到type 的值: 游乐场的例子 该转换仅在基础类型,除了结构标签相同的工作。

  • 两个节点中的配置均为: 节点中的容器运行时使用: 节点中的容器使用以下方式运行: VM在GCP上是新鲜的,并且在同一个网络上,我使用了以下镜像:

  • 我使用一个Android库,它要求我创建两个类,每个类继承自不同的类 (具有公共基类) 现在我有这个代码: 我必须复制这个类来创建一个扩展,即使我的两个类共享完全相同的代码。 我简化了示例的代码,但重复可能很重要 我决定将代码放在这两个类之外的静态方法中,并在类重写的方法中调用它们,但我认为必须有一种更干净的方法来做到这一点。 你能帮我解决这个问题吗? 和都继承自。

  • 问题内容: 我正在一个Swift操场上玩,正在上一堂新课。由于某种原因,我不断收到这样的错误:类“没有成员类型”,其名称的常量前面定义了三行。这是代码: Xcode Beta6一直在倒数第二行给我一个错误,说“ DataModel.Type没有名为’myCalendar’的成员 尽管我认为这不会有所作为,但我尝试将myCalendar定义为var。 问题答案: 您无法初始化引用同一类的另一个实例属

  • 我想通过类型使用自动连接,以便在运行时容器注入适当的对象并调用适当的bean/method。 1.接口 2.头等舱 4.Rest课 5.application-context.xml 错误:

  • 我正在尝试合并两个不同类型的列表。我得到了两个不同的API响应从改造在一个android应用程序,第一个列表是一个电影列表定义为 公共类列表{ 我通过以下操作成功地从API中检索到列表 然后,我尝试将一个类型列表和一个类型字符串列表组合起来,以创建一个新类ListingAndImage(字符串是一个URL,我将加载到imageview中) 我的问题是,哪种方式最好将这两个列表组合起来,形成一个列表