问题描述
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;
};
本质上是基于一些 something
ean 标志构建 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);
}
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 给出了很好的理由,说明如果您不直接使用原始类型,您的代码会更强大,原因正是您已经确定的。