2017-05-23 9 views
1

これはUsing std::forward on sub fieldsと同様の質問ですが、私の場合は答えがないようです。キャストされた引数にstd :: forwardを使用する

Consider this code:

template<class Base, class F> 
void visit(Base&&, const F&) { 
    throw std::bad_cast(); 
} 

template<class Derived, class... Rest, class Base, class F> 
void visit(Base&& base, const F& f) { 
    if (auto *as_derived = dynamic_cast<Derived *>(&base)) { 
     return f(std::forward<Base>(*as_derived)); 
    } else { 
     return visit<Rest...>(std::forward<Base>(base), f); 
    } 
} 

私の目標は、次のテストケースの作品ということである。

struct Animal { 
    virtual ~Animal() {} 
}; 
struct Cat : Animal { 
    void speak() & { puts("meow"); } 
    void yowl() && { puts("MEOW!"); } 
}; 
struct Dog : Animal { 
    void speak() & { puts("woof"); } 
    void yowl() && { puts("WOOF!"); } 
}; 

int main() { 
    Animal *a = new Cat(); 
    Animal *b = new Dog(); 
    visit<Cat, Dog>(*a, [](auto&& a){ std::forward<decltype(a)>(a).speak(); }); 
    visit<Cat, Dog>(*b, [](auto&& a){ std::forward<decltype(a)>(a).speak(); }); 
    visit<Cat, Dog>(std::move(*a), [](auto&& a){ std::forward<decltype(a)>(a).yowl(); }); 
    visit<Cat, Dog>(std::move(*b), [](auto&& a){ std::forward<decltype(a)>(a).yowl(); }); 
} 

所望の出力: "!MEOW" "ニャー" "横糸" "横糸!"。 speakyowlは仮想ではないことに注意してください。これらは実際にはテンプレートであり、テンプレートは仮想ではないため、これは私の元のコードで必要です。

std::forward<Base>(*as_derived)は、完璧な転送を可能にするために*as_derivedのref-qualifierとconst-qualifierを単に変更するだけではありません。 タイプを実際にBase&にキャストし、visitの全体を突き止める!

は、私はstd::forwardがやりたい何が標準ライブラリ関数はあります - つまり、完璧な-前方std::forward<Base>から推定されることになるものと一致する*as_derivedにREF-修飾子とのconst修飾子を変更しますか?

標準ライブラリ機能がない場合、私は自分のために「完全な子タイプ」関数をどのように書くことができますか?

上記のWandboxのリンクには、このテストケースで「機能する」ものが含まれていますが、constnessは保持されず、まったく上手く見えません。

答えて

4

これには標準で何もありません。しかし、書くのは難しくない。ちょうど迷惑。

:基本的にあなたが Baseが何であるかに DerivedのCV-資格およびリファレンスネスにマッチし、次に forwardにそのタイプを渡したい - 何をする必要がある forwardに渡すためにあなたのタイプを与える形質を書きです
return f(std::forward<match_ref_t<Base, Derived>>(*as_derived)); 

ほぼ確実に、より簡潔に行うことができる簡単な実装は、ちょうどです:

template <class From, class To> 
struct match_ref { 
    using type = To; 
}; 

template <class From, class To> 
using match_ref_t = typename match_ref<From, To>::type; 

template <class From, class To> 
struct match_ref<From&, To> { 
    using type = match_ref_t<From, To>&; 
}; 

template <class From, class To> 
struct match_ref<From&&, To> { 
    using type = match_ref_t<From, To>&&; 
}; 

template <class From, class To> 
struct match_ref<From const, To> { 
    using type = match_ref_t<From, To> const; 
}; 

template <class From, class To> 
struct match_ref<From volatile, To> { 
    using type = match_ref_t<From, To> volatile; 
}; 

template <class From, class To> 
struct match_ref<From const volatile, To> { 
    using type = match_ref_t<From, To> const volatile; 
}; 

それとも、私が推測する:

template <class Check, template <class> class F, class T> 
using maybe_apply = std::conditional_t<Check::value, F<T>, T>; 

template <class From, class To> 
struct match_ref { 
    using non_ref = std::remove_reference_t<From>; 
    using to_cv = maybe_apply<std::is_const<non_ref>, std::add_const_t, 
      maybe_apply<std::is_volatile<non_ref>, std::add_volatile_t, 
      To>>; 

    using type = std::conditional_t< 
     std::is_lvalue_reference<From>::value, 
     to_cv&, 
     std::conditional_t< 
      std::is_rvalue_reference<From>::value, 
      to_cv&&, 
      to_cv> 
     >; 
}; 

template <class From, class To> 
using match_ref_t = typename match_ref<From, To>::type; 
2

フォワードは単なる条件付き移動です。

template<bool b> 
struct move_if_t{ 
    template<class T> 
    T&& operator()(T&t)const{ return std::move(t); } 
}; 
template<> 
struct move_if_t<false>{ 
    template<class T> 
    T& operator()(T&t)const{ return t; } 
}; 
template<bool b, class T> 
decltype(auto) move_if(T& t){ 
    return move_if_t<b>{}(t); 
} 

今、私たちは、私はmatch_cvref_tがよりについて推論するためにはるかに簡単であることがわかったので、

template<class Derived, class... Rest, class Base, class F> 
void visit(Base&& base, const F& f) { 
    if (auto *as_derived = dynamic_cast<Derived *>(&base)) { 
    return f(move_if<!std::is_lvalue_reference<Base>{}>(*as_derived)); 
    } else { 
    return visit<Rest...>(std::forward<Base>(base), f); 
    } 
} 
+0

これは、すべてのcv-refのものを列挙するよりもずっとうれしいです。 – Barry

0

Yakkの答えは、見事に簡潔で、動作しているようですが、私は実際にはバリーの答えと一緒に行くことになった取得いずれかの選択肢。さらに私の場合、実際の鋳造作業を正しく行うためには、とにかくmatch_cvref_tを参照する必要がありました。したがって:

template<class Base, class F> 
void visit(Base&&, const F&) { 
    throw std::bad_cast(); 
} 

template<class DerivedClass, class... Rest, class Base, class F> 
void visit(Base&& base, const F& f) { 
    if (typeid(base) == typeid(DerivedClass)) { 
     using Derived = match_cvref_t<Base, DerivedClass>; 
     return f(std::forward<Derived>(static_cast<Derived&&>(base))); 
    } else { 
     return visit<Rest...>(std::forward<Base>(base), f); 
    } 
} 

私は

match_cv_tmatch_ref_tは、コードの約5行ごとを取る
template<class From, class To> 
using match_cvref_t = match_ref_t< 
    From, 
    match_cv_t< 
     std::remove_reference_t<From>, 
     std::remove_reference_t<To> 
    > 
>; 

までmatch_cvref_tを縮小するために管理しました。

関連する問題