【问题标题】:Zero-sized member variable in templated struct模板结构中的零大小成员变量
【发布时间】:2018-10-08 14:08:27
【问题描述】:

我有一个模板结构,它有一个带有额外浮点数的变体,如下所示:

template <bool HasFloat>
struct Foo {
   std::vector<int> a;
   float b; // Not needed if HasFloat is false
};

为了节省内存(是的,这很重要)如果HasFloat 为假,我想省略该浮点数。由于struct 中还有很多其他内容,因此最好的方法是这样的:

using B = typename std::conditional<HasFloat, float, ZeroSizedType>::type;
B b;

据我所知,除了 C++ 中没有零大小的类型。唯一的例外似乎是“灵活数组成员”,所以我也许可以这样做:

using B = typename std::conditional<HasFloat, float, float[]>::type;

除了它们仅在 C99 中受支持,而不是 C++。

对此的标准解决方案似乎是使用继承,因为基类可以是零大小的,但是我的结构也可以通过程序集访问,并且为了使程序集更简单,如果float b; 位于结构的末尾而不是开头,无论如何that isn't guaranteed

所以这似乎将模板专业化作为唯一的选择,但我的课程实际上相当长,我想避免重复所有内容。我还缺少其他解决方案吗?

【问题讨论】:

  • 你可以把所有不依赖于特化的东西放到基类中,然后有两个继承自基类的版本——一个专门用于假情况,另一个不。
  • 看不到倒数第二段。
  • 你似乎在围栏的某个地方。您想要有保证的布局(该标准并不能真正保证),但同时,您似乎想要严格的标准一致性(否则您可以只使用您的 fav 编译器的扩展来实现灵活的数组成员或零大小数组)跨度>
  • 不是答案,但 C++20 将具有 [[no_unique_address]] 属性以允许空结构成员的大小为零。
  • @Timmmm 不是真的,例如,成员之间可能存在未指定的填充。

标签: c++ templates


【解决方案1】:

我的一位同事提出了一个相当不错的解决方案。它确实需要复制和粘贴数据成员,但至少我不必复制我的方法。

template <bool HasFloat>
struct Foo {
  struct State {
    std::vector<int> a;
  };

  struct StateWithFloat {
    std::vector<int> a;
    float k;
  };

  using FooState = std::conditional_t<HasFloat, StateWithFloat, State>;
  FooState state;
};

你可以这样做:

  struct StateWithFloat {
    State state;
    float k;
  };

但是你必须添加模板函数以避免 state.astate.state.a 的问题,此时复制和粘贴似乎更容易。

@aschepler 还指出,在 C++20 中,您将能够使用 [[no_unique_address]]

【讨论】:

    【解决方案2】:

    您可以创建一个专门针对是否具有浮点数的结构,然后在您的结构中使用它来转发模板参数。像这样的:

    template <bool HasFloat>
    struct PerhapsFloat;
    
    template <>
    struct PerhapsFloat<true>
    {
        float b;
    };
    
    template <>
    struct PerhapsFloat<false>
    {
    
    };
    
    template <bool HasFloat>
    struct Foo {
       std::vector<int> a;
       PerhapsFloat<HasFloat> b;
    };
    

    在这里,您有一个演示: https://godbolt.org/z/7zPto9

    【讨论】:

    • 这仍然会使用至少 1 个字节,OP 很清楚这一点。这对问题没有任何帮助。
    • 为什么你没有检查你自己的代码。对我来说,这两个结构的大小都是 32。如果你准备一个例子,也在 Godbolt 上,你为什么不运行它?
    【解决方案3】:

    尝试将特化提取到基类/成员类。

    template <bool B>
    struct Foo {};
    
    template <>
    struct Foo<true>
    {
        float f;    
    };
    
    template <bool HasFloat>
    class Bar 
    {
        Foo<HasFloat> b;
    };
    
    class Empty {};
    
    int main()
    {
        std::cout << sizeof(Bar<true>) << std::endl; // 4
        std::cout << sizeof(Bar<false>) << std::endl; // 1
        std::cout << sizeof(Empty) << std::endl; // 1
    }
    

    【讨论】:

    • 空结构的大小不为零。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-21
    • 1970-01-01
    • 2019-07-28
    相关资源
    最近更新 更多