【问题标题】:C++ make member object unnamedC ++使成员对象未命名
【发布时间】:2018-02-21 22:17:57
【问题描述】:

我想编写方便的颜色管理类,这将允许我使用不同的组件顺序和基本上不同的设置。我希望它在编译时是可区分的。 假设我有这个代码:

template <typename _valueType>
struct RGBAColorData {
    using ValueType = _valueType;
    union {
        struct { ValueType r, g, b, a; };
        ValueType components[4];
    };
};

这个(即使匿名结构是非标准的)工作正常,当我想这样使用它时:

RGBAColorData color;
color.r = whatever;

但是,这不是我的代码的最终形式。我希望它有“拥有”类模板,它会在编译时在XYZColorData 之间进行选择。假设它看起来像这样:

template <typename _valueType, template <typename> _dataScheme>
struct Color
{
    using ValueType = _valueType;
    using DataScheme = _dataScheme<ValueType>;

    // what now?
    // DataScheme data; // ???
};

这会产生问题,因为我希望我的代码可以这样使用:

using RGBAColorF = Color<float, RGBAColorData>;
RGBAColorF brushColor;
brushColor.r = whatever;

这将是使用颜色的一种非常方便的方式,但是我想不出任何解决此问题的方法。 最后,也许我对此有错误的方法,也许这可以用更少的努力来完成,但是我想不出任何其他不涉及大量模板类专业化的方法。

【问题讨论】:

  • 你需要继承。
  • 不幸的是,您的“工作正常”代码具有未定义的行为,我几乎可以肯定您打算使用它。
  • @Walter:与其说是严格的别名规则,不如说是“从非活动的联合成员中读取”规则。相关:stackoverflow.com/q/36051084/103167
  • 这里有两个问题。您问的那个,我将其解释为“如何重用变体成员的集合?”,有一个简单的答案:使用继承。在 cmets 中提出的问题是,“当我使用联合在数组顶部覆盖具有多个相同类型元素的结构时,我可以互换使用结构成员名称和数组索引吗?”有一个更复杂的答案:不,在你看来它可能有效,但它并不安全。

标签: c++ member anonymous


【解决方案1】:

别这样!

为了获得一些不错的句法效果而四处招摇是充满危险的,并且可能会抹杀未来的进化。

首先,在 C++ 中,只有一个联合成员可以在任何时候处于活动状态。因此,不能保证切换数组和结构的使用,即使在许多编译器上这可能会导致预期的结果。

然后,不能保证结构成员是连续的,因此如果混合使用数组和结构可以工作,它可能仍然不会导致正确的结果,即使在许多编译器上这将按预期工作。

或者用更安全的方法来做......

如果你仍然喜欢混合使用特定的颜色分量 r、g、b 和数组,你应该考虑一种更安全的方法:

template <typename _valueType>
struct RGBAColorData {
    using ValueType = _valueType;
    ValueType components[4];
    ValueType &r=components[0], &g=components[1], 
                   &b=components[2], &a=components[3]; // ATTENTION (see explanations)
};

注意:我做得又快又脏。您应该更好地实现三规则,使用正确的构造函数、复制构造函数和赋值运算符,以确保引用不会混乱。

我不太喜欢这个解决方案,但它可以安全地工作 (online demo):诀窍是让 r、g、b 成为对特定数组项的引用。那么你就确定你可以混合访问方式,并且你绝对确定两者之间的映射是正确的。

但更喜欢干净的封装

您最初的方法和我的解决方法的问题是它们破坏了封装:您必须知道颜色的内部结构才能使用它。

使用这种方法,您将永远无法进化。例如切换到 CMYK 颜色方案,或采用位字段编码会受到影响。

正确的方法是使用一组 getter 和 setter 将内部结构完全隐藏到外部世界。当然,从语法上来说它看起来不太好,但是你真的可以编写真正通用的颜色代码,其中编码方案可以是编译时策略。

【讨论】:

  • sn-p 中的解决方案有一些显着的内存开销。
  • 添加引用会使结构变大 - 根据架构,它甚至可以添加 32 个额外字节。我的类应该与图形 API 使用的着色器一起使用,因此我需要它在编译时可区分。您如何想象封装 RGBA 和 CMYK 以便以类似的方式使用它们?模板的使用保证了复制安全并且不允许隐式转换。您将始终知道颜色使用什么类型和方案。
  • @razzorflame 我刚刚展示了一种方法来确保您想要的接口符合标准,而不是现在工作但可能在编译器的每个版本中被破坏的 UB。如前所述,我不推荐这种方法。如果我是你,我会选择一种访问方式(单个组件或数组)并始终如一地使用它,或者更好的是,使用 getter 和 setter:主流编译器会内联此类代码,因此不会造成性能开销。
【解决方案2】:

最后,我决定使用继承(正如 Ben Voigt 所说)。 我使用此答案提出的绝妙方法解决了另一个问题,联合使代码不安全:
https://stackoverflow.com/a/494760/4386320

【讨论】:

    猜你喜欢
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    • 1970-01-01
    相关资源
    最近更新 更多