注:この質問はthisの複製として簡単にマークされましたが、特に std::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
本当に必要な場合は、もう1つのレベル( でオーバーロード)を追加できますが、それを超えるものはとにかくひどいコードです)。
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;
}
}
ノート:
- 私はより良いオプション
std::optional
に切り替えたくありません。 - 参照をサポートしていないため、コピーを作成する必要があるポインターを返さない限り、perf についてはあまり気にしません (arg が一時的でない限り)
std::optional
。 - ネストされたオプションをうまく
flatten_has_value
フラット化する方法があれば、その関数を書く方法もあるからです。 - 私のコードは機能しているように見えますが、かなり醜いので、より良い解決策があるかどうか疑問に思っています。