注意:这个问题曾经被暂时标记为此问题的重复,但它并不是完全相同的,因为我特别询问std::optional。如果你关心一般情况,那么阅读这个问题仍然是一个好主意。
假设我有嵌套的optionals,类似于这样(愚蠢的玩具例子):
struct Person{
const std::string first_name;
const std::optional<std::string> middle_name;
const std::string last_name;
};
struct Form{
std::optional<Person> person;
};
还有这个垃圾功能:
void PrintMiddleName(const std::optional<Form> form){
if (form.has_value() && form->person.has_value() && form->person->middle_name.has_value()) {
std::cout << *(*(*form).person).middle_name << std::endl;
} else {
std::cout << "<none>" << std::endl;
}
}
如何最好地简化这个可选项检查?我做了类似于这样的东西,它不是可变参数的,但我并不太在意(如果真的需要,我可以添加一个更高级别的重载membr3
),而且超出那个范围的所有代码都很糟糕。
template<typename T, typename M>
auto flatten_opt(const std::optional<T> opt, M membr){
if (opt.has_value() && (opt.value().*membr).has_value()){
return std::optional{*((*opt).*membr)};
}
return decltype(std::optional{*((*opt).*membr)}){};
}
template<typename T, typename M1, typename M2>
auto ret_val_helper(){
// better code would use declval here since T might not be
// default constructible.
T t;
M1 m1;
M2 m2;
return ((t.*m1).value().*m2).value();
}
template<typename T, typename M1, typename M2>
std::optional<decltype(ret_val_helper<T, M1, M2>())> flatten_opt(const std::optional<T> opt, M1 membr1, M2 membr2){
if (opt.has_value() && (opt.value().*membr1).has_value()){
const auto& deref1 = *((*opt).*membr1);
if ((deref1.*membr2).has_value()) {
return std::optional{*(deref1.*membr2)};
}
}
return {};
}
void PrintMiddleName2(const std::optional<Form> form){
auto flat = flatten_opt(form, &Form::person, &Person::middle_name);
if (flat) {
std::cout << *flat;
}
else {
std::cout << "<none>" << std::endl;
}
}
注意:
- 我不想切换到更好的可选项 (TartanLlama/optional)。
- 我不太在意性能,除非返回指针,因为除非参数是临时的,否则必须进行拷贝(因为
std::optional
不支持引用)。 - 虽然
flatten_has_value
函数很有用,但我不太关心它,因为如果有一种方法可以很好地展开嵌套的可选项,那么也有一种方法编写该函数。 - 我知道我的代码看起来像是正常工作了,但实际上它相当丑陋,所以我想知道是否有更好的解决方案。
if (form.has_value() && form->person.has_value() && form->person->middle_name.has_value())
可以改为if (form && form->person && form->person->middle_name)
。一个更少冗长的*(*(*form).person).middle_name
可以改为form->person->middle_name
。 - Drew Dormannstd::string
时很少需要使用std::optional
。特别是在这种情况下,没有必要区分缺失的中间名和空白的中间名。因此,我会将const std::optional<std::string> middle_name;
更改为const std::string middle_name;
,然后使用if (form && form->person && !form->person->middle_name.empty()) { */ use form->person->middle_name as needed */ }
。 - Remy Lebeaumiddle_name
。如果middle_name
的值为空字符串和middle_name
没有值有什么区别?如果答案是“永远没有区别”,如果你的代码从未将这两种情况视为不同,那么optional
就不应该是这个属性的类型。 - Nicol Bolasstd::optional
。 - Casey