微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将几个布尔标志包装到结构中以将它们传递给具有方便语法的函数

如何解决如何将几个布尔标志包装到结构中以将它们传递给具有方便语法的函数

在一些测试代码中有一个这样的辅助函数

auto make_condiment(bool salt,bool pepper,bool oil,bool garlic) {
    // assumes that first bool is salt,second is pepper,// and so on...
    //
    // Make up something according to flags
    return something;
};

本质上是基于一些 somethingean 标志构建 bool

让我担心的是每个 bool 的含义都硬编码在参数名称中,这很糟糕,因为在调用站点很难记住哪个参数意味着什么(是的,IDE 可能会消除完全通过在选项卡完成时显示这些名称解决问题,但仍然......):

// at the call site:
auto obj = make_condiment(false,false,true,true); // what ingredients am I using and what not?

因此,我想传递一个描述设置的对象。此外,只需将它们聚合在一个对象中,例如std::array<bool,4>

相反,我想启用这样的语法:

auto obj = make_smart_condiment(oil + garlic);

这将生成与之前对 obj调用相同的 make_condiment

这个新功能是:

auto make_smart_condiment(Ingredients ingredients) {
    // retrieve the individual flags from the input
    bool salt = ingredients.hasSalt();
    bool pepper = ingredients.hasPepper();
    bool oil = ingredients.hasOil();
    bool garlic = ingredients.hasGarlic();
    // same body as make_condiment,or simply:
    return make_condiment(salt,pepper,oil,garlic);
}

Here's my attempt

struct Ingredients {
  public:
    enum class INGREDIENTS { Salt = 1,Pepper = 2,Oil = 4,Garlic = 8 };
    explicit Ingredients() : flags{0} {};
    explicit Ingredients(INGREDIENTS const& f) : flags{static_cast<int>(f)} {};
  private:
    explicit Ingredients(int fs) : flags{fs} {}
    int flags; // values 0-15
  public:
    bool hasSalt() const {
        return flags % 2;
    }
    bool hasPepper() const {
        return (flags / 2) % 2;
    }
    bool hasOil() const {
        return (flags / 4) % 2;
    }
    bool hasGarlic() const {
        return (flags / 8) % 2;
    }
    Ingredients operator+(Ingredients const& f) {
        return Ingredients(flags + f.flags);
    }
}
salt{Ingredients::INGREDIENTS::Salt},pepper{Ingredients::INGREDIENTS::Pepper},oil{Ingredients::INGREDIENTS::Oil},garlic{Ingredients::INGREDIENTS::Garlic};

然而,我有一种自己在重新发明轮子的感觉。

  • 是否有更好的或标准的方法来完成上述任务?

  • 是否有我可以/应该使用的设计模式?

解决方法

我认为您可以使用 std::bitset 删除一些样板文件。这是我想出的:

#include <bitset>
#include <cstdint>
#include <iostream>

class Ingredients {
public:
    enum Option : uint8_t {
        Salt = 0,Pepper = 1,Oil = 2,Max = 3
    };

    bool has(Option o) const { return value_[o]; }

    Ingredients(std::initializer_list<Option> opts) {
        for (const Option& opt : opts)
            value_.set(opt);
    }

private:
    std::bitset<Max> value_ {0};
};

int main() {
    Ingredients ingredients{Ingredients::Salt,Ingredients::Pepper};
    
    // prints "10"
    std::cout << ingredients.has(Ingredients::Salt)
              << ingredients.has(Ingredients::Oil) << "\n";
}

您没有获得 + 类型语法,但它非常接近。不幸的是,您必须保留一个 Option::Max,但还不错。此外,我决定使用 enum class,以便它可以作为 Ingredients::Salt 访问并隐式转换为 int。如果您想使用 enum class,您可以显式访问和转换。

,

如果您想使用枚举作为标志,通常的方法是将它们与 operator | 合并并使用 operator & 进行检查

#include <iostream>

enum Ingredients{ Salt = 1,Pepper = 2,Oil = 4,Garlic = 8 };

// If you want to use operator +
Ingredients operator + (Ingredients a,Ingredients b) {
    return Ingredients(a | b);
}

int main()
{
    using std::cout;
    cout << bool( Salt & Ingredients::Salt   ); // has salt
    cout << bool( Salt & Ingredients::Pepper ); // doesn't has pepper

    auto sp = Ingredients::Salt + Ingredients::Pepper;
    cout << bool( sp & Ingredients::Salt     ); // has salt
    cout << bool( sp & Ingredients::Garlic   ); // doesn't has garlic
}

注意:如果将 operator +|+ 一样混合使用,当前代码(只有 (Salt|Salt)+Salt)将不起作用。


您也可以使用enum class,只需要define the operators

,

我会考虑一个强类型库,例如:

https://github.com/joboccara/NamedType

关于这个的非常好的视频:

https://www.youtube.com/watch?v=fWcnp7Bulc8

第一次看到这个,我有点不屑一顾,但因为建议来自我尊重的人,所以我给了它一个机会。视频说服了我。

如果您查看 CPP Best Practices 并进行足够深入的挖掘,您将看到避免使用布尔参数(尤其是它们的字符串)的一般建议。 Jonathan Boccara 给出了很好的理由,说明如果您不直接使用原始类型,您的代码会更强大,原因正是您已经确定的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。