19

注:この質問は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フラット化する方法があれば、その関数を書く方法もあるからです。
  • 私のコードは機能しているように見えますが、かなり醜いので、より良い解決策があるかどうか疑問に思っています。
4

3 に答える 3

5

基本的に連鎖可能な操作である何かに対して、多くのヘルパー関数があります。そして、C++ にはチェーン用のものがあります: 演算子です。だから私はおそらくこれを(ab)useoperator*します。

特定のケースでは、必要なのは

template<class class_t, class member_t>
std::optional<std::remove_cv_t<member_t>> operator*(
        const std::optional<class_t>& opt, 
        const std::optional<member_t> class_t::*member) 
{
    if (opt.has_value()) return opt.value().*member;
    else return {};
}

void PrintMiddleName2(const std::optional<Form> form){
    auto middle = form * &Form::person * &Person::middle_name;
    if (middle) {
        std::cout << *middle;
    }
    else {
        std::cout << "<none>"  << std::endl; 
    }
}

しかし実際には、オプションではないメンバー、getter メソッド、およびここにリストした任意の変換のバリアントも必要になるでしょうが、それらがすべて適切にコンパイルされるとは 100% 確信していません。

//data member
template<class class_t, class member_t>
std::optional<std::remove_cv_t<member_t>> operator*(const std::optional<class_t>& opt, const std::optional<member_t> class_t::*member) {
    if (opt.has_value()) return opt.value().*member;
    else return {};
}
template<class class_t, class member_t>
std::optional<std::remove_cv_t<member_t>> operator*(const std::optional<class_t>& opt, const member_t class_t::*member) {
    if (opt.has_value()) return {opt.value().*member};
    else return {};
}

//member function
template<class class_t, class return_t>
std::optional<std::remove_cv_t<return_t>> operator*(const std::optional<class_t>& opt, std::optional<return_t>(class_t::*member)()) {
    if (opt.has_value()) return opt.value().*member();
    else return {};
}
template<class class_t, class return_t>
std::optional<std::remove_cv_t<return_t>> operator*(const std::optional<class_t>& opt, return_t(class_t::*member)()) {
    if (opt.has_value()) return {opt.value().*member()};
    else return {};
}

//arbitrary function
template<class class_t, class return_t, class arg_t>
std::optional<std::remove_cv_t<return_t>> operator*(const std::optional<class_t>& opt, std::optional<return_t>(*transform)(arg_t&&)) {
    if (opt.has_value()) return transform(opt.value());
    else return {};
}
template<class class_t, class return_t, class arg_t>
std::optional<std::remove_cv_t<return_t>> operator*(const std::optional<class_t>& opt, return_t(*transform)(arg_t&&)) {
    if (opt.has_value()) return {transform(opt.value())};
    else return {};
}

http://coliru.stacked-crooked.com/a/26aa7a62f38bbd89

于 2021-04-02T17:37:04.087 に答える