2016-10-09 11 views
-1

我想在C++中擁有dynamic_call功能。它應該觸發重載解析並根據參數的動態類型調用目標類(Visitor)上最具體的函數。它應該替換訪問者模式,並應像C#中的動態關鍵字一樣工作。 我粘貼了我到目前爲止的下面。我想不必在調用方聲明通用lambda,而是在動態調用的實現上使它更易於使用。這可能嗎?C++中的動態分派 - 更好的語法

#include <iostream> 

struct Base { virtual ~Base() = default; }; 
class A : public Base {}; 
class B : public Base {}; 

template <class... Ts> 
class dynamic_call { 
public: 
    template <class F, class Arg> 
    static void call(F& func, Arg& a) { 
     call_impl<F, Arg, Ts...>(func, a); 
    } 
private: 
    template <class F, class Arg> 
    static void call_impl(F& /*func*/, Arg& /*a*/) { 
     //end of recursion => nothing more to be done 
    } 
    template <class F, class Arg, class T, class... R> 
    static void call_impl(F& func, Arg& a) { 
     T* t = dynamic_cast<T*>(&a); 
     if(t) { 
      func(*t); 
     } 
     call_impl<F, Arg, R...>(func, a); 
    } 
}; 

using namespace std; 
struct Visitor { 
    void Visit(A&) { cout << "visited for a" << endl; } 
    void Visit(B&) { cout << "visited for b" << endl; } 
}; 

int main(int /*argc*/, char */*argv*/[]) 
{ 
    Visitor v; 
    auto func = [&v](auto& a) { v.Visit(a); }; 

    A a; 
    dynamic_call<A, B>::call(func, a); 

    B b; 
    dynamic_call<A, B>::call(func, b); 

    { 
     Base& base(a); 
     dynamic_call<A, B>::call(func, base); 
    } 
    { 
     Base& base(b); 
     dynamic_call<A, B>::call(func, base); 
    } 

    return 0; 
} 

我想這樣調用它,而不需要添加通用lambda。

dynamic_call<A,B>::call(v, a); 
+0

使用'std :: function'可能是。您的問題不清楚 –

+0

我是否正確理解您基本上希望'Visit'(或對其進行封裝調用)表現得像是'Base'的虛擬成員函數?關於你的最後一項要求:在'Visitor'中將'Visit'改爲'operator()',你的行就可以工作。 – user4407569

+0

@eichhörnchennope我想調用正確的實現訪問取決於參數的運行時類型。這不是一個正常的虛擬電話。 –

回答

2

這裏有一些想法,我不知道你的要求是什麼,所以他們可能不適合:

  1. 變化Visitoperator()。然後,根據您的要求,調用語法會減少爲dynamic_call<A,B>::call(v, a);。當然,這隻有在訪問者的界面可能改變的情況下才有可能。

  2. 更改func(*t)call_implfunc.Visit(*t)。然後再次呼叫者可以使用dynamic_call<A,B>::call(v, a);並且不需要改變訪問者的界面。但是,每個使用​​的訪問者現在需要將Visit定義爲訪問者方法。我認爲operator()更清潔並且遵循通常的模式,例如用於標準庫中的Predicates更多。

我特別不喜歡其中任一,因爲主叫方總是知道在訪問者提供可能的過載,並一直使用​​記住。因此,我建議在遊客結構來解決所有問題:

  • struct Visitor { 
        void Visit(A&) { 
         cout << "visited for a" << endl; 
        } 
        void Visit(B&) { 
         cout << "visited for b" << endl; 
        } 
        void Visit(Base& x) { 
         dynamic_call<A,B>::call([this](auto& x){Visit(x);}, x); 
        } 
    }; 
    

    這可以用v.Visit(a)v.Visit(b)v.Visit(base)被調用。這樣,Visitor的用戶不需要知道關於不同派生類的變化行爲的任何信息。

  • 如果你不想修改Visitor,那麼你可以通過繼承添加過載:

    struct DynamicVisitor : Visitor { 
        void Visit(Base& x) { 
         dynamic_call<A,B>::call([this](auto& x){Visit(x);}, x); 
        } 
    }; 
    
  • 的點可以結合使用,例如爲:

    struct Visitor { 
        void operator()(A&) { 
         cout << "visited for a" << endl; 
        } 
        void operator()(B&) { 
         cout << "visited for b" << endl; 
        } 
        void operator()(Base& x) { 
         dynamic_call<A,B>::call(*this, x); 
        } 
    }; 
    

    用作v(a)v(b)v(base)

    +0

    這絕對是有趣的 –

    +0

    花了我一些時間來弄清楚這是如何工作的,但這是我正在尋找 –