2011-03-16 33 views
3

鑑於類Foo重構C++模板類

template <typename T> 
class Foo 
{ 
public: 

    ...other methods.. 

    void bar() 
    { 
    ... 
    m_impl.doSomething(); 
    ... 
    } 

    void fun() 
    { 
    ... 
    m_impl.doSomethingElse(); 
    ... 
    } 

    void fubar() 
    { 
    ... 
    } 

private: 
    T m_impl; 
}; 

我想,以滿足其中T是一個boost :: shared_ptr的情況。 在這種情況下爲類Foo唯一的變化是,它應該調用

m_impl->doSomething(); 

,而不是

m_impl.doSomething(); 

我結束了在相同的標題定義FooPtr現在雖然

template <typename T> 
class FooPtr 
{ 
public: 
    ...other methods.. 

    void bar() 
    { 
    ... 
    m_pImpl->doSomething(); 
    ... 
    } 

    void fun() 
    { 
    ... 
    m_pImpl->doSomethingElse(); 
    ... 
    } 

    void fubar() 
    { 
    ... 
    } 

private: 
    boost::shared_ptr<T> m_pImpl; 
}; 

方法適用於我想用於Foo的所有類, 問題是我有很多重複的代碼,並且有任何更改 我使Foo,我也必須使FooPtr。

我該如何重構代碼?例如。有什麼方法可以在編譯時確定T是否爲boost :: shared_ptr類型,然後專門調用bar和fun方法來調用 - >運算符?

編輯: 感謝迄今爲止所有的答案!我只需要一些時間來解決所有問題,並查看哪種解決方案最適合我們的軟件。

編輯2: @Matthieu:這是我用

class FooImpl 
{ 
public: 
    void doIt() 
    { 
    cout << "A" << std::endl; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    Foo<FooImpl> foo; 
    foo.doSomething(); 
    return 0; 
} 

回答

4

Sylvain寫了一個DRY解決方案,但我不喜歡濫用繼承。

使用包裝類來統一界面很容易,特別是因爲指針語義工作得很好!

namespace details { 
    template <typename T> 
    struct FooDeducer { 
    typedef boost::optional<T> type; 
    }; 

    template <typename T> 
    struct FooDeducer< T* > { 
    typedef T* type; 
    }; 

    template <typename T> 
    struct FooDeducer< boost::shared_ptr<T> > { 
    typedef boost::shared_ptr<T> type; 
    }; 
} // namespace details 

template <typename T> 
class Foo { 
public: 
    // methods 
    void doSomething() { impl->doIt(); } 

private: 
    typedef typename details::FooDeducer<T>::type Type; 
    Type impl; 
}; 

這裏,依靠boost::optional它提供了OptionalPointee語義,我們幾乎得到比指針相同的行爲。

但我想強調的一點是複製行爲的差異。 boost::optional提供深層複製。

+0

我只是試圖使用你的方法,但從optional.hpp中得到一個斷言:this-> is_initialized()失敗。我以前從未使用過可選項,並已將測試代碼添加到該問題中。我錯過了什麼? – Ralf 2011-03-16 16:12:03

+0

@Ralf:默認情況下,'boost :: optional'是空的(就像一個空指針),如果你想要一個默認值,你需要在初始化列表中明確地給它賦一個值,例如'impl(T())'構建的對象。 – 2011-03-16 16:16:17

+0

啊,好的,謝謝,這就是訣竅:) – Ralf 2011-03-17 06:31:18

0

您可以使用部分專業化的測試代碼。

template <typename T> 
class Foo 
{ 
public: 
    //... 
}; 

template<typename T> class Foo<boost::shared_ptr<T>> { 
    //... implement specialization here 
}; 
1

可以引入另一箇中間模板類,類似的東西:現在

template < typename T > 
class FooBase 
{ 
private: 
    T m_impl; 

protected: 
    T& impl() { return m_impl; } 
}; 

template < typename T > 
class FooBase< boost::shared_ptr<T> > 
{ 
private: 
    boost::shared_ptr<T> m_impl; 

protected: 
    T& impl() { return *(m_impl.operator ->()); } 
}; 

template < typename T > 
class Foo : protected FooBase<T> 
{ 
public: 
    void bar() 
    { 
     impl().DoSomething(); 
    } 
}; 

,你只需要一次編寫的Foo類。並且您可以通過對FooBase進行部分特化來專門爲其他智能指針類型進行分類。

編輯:您還可以使用組成,而不必FooFooBase之間的繼承關係(在這種情況下,我可能會重命名爲FooHelper或類似的東西)。

template < typename T > 
class FooHelper 
{ 
private: 
    T m_impl; 

public: 
    T& impl() { return m_impl; } 
}; 

template < typename T > 
class FooHelper< boost::shared_ptr<T> > 
{ 
private: 
    boost::shared_ptr<T> m_impl; 

public: 
    T& impl() { return *(m_impl.operator ->()); } 
}; 

template < typename T > 
class Foo 
{ 
private: 
    FooHelper<T> m_helper; 

public: 
    void bar() 
    { 
     m_helper.impl().DoSomething(); 
    } 
}; 
+1

我想盡量減少重複的想法,但我不能不認爲這是繼承和組合的濫用會更適合的任務。 – 2011-03-16 12:17:14

2
class A 
{ 
public: 
    void doSomething() {} 
}; 
template <typename T> 
class Foo 
{ 
public: 
    void bar() 
    { 
    Impl(m_impl).doSomething(); 
    } 

private: 
    template<typename P> 
    P& Impl(P* e) 
    { 
     return *e; 
    } 
    template<typename P> 
    P& Impl(std::shared_ptr<P> e) 
    { 
     return *e; 
    } 
    template<typename P> 
    P& Impl(P& e) 
    { 
     return e; 
    } 
    T m_impl; 
}; 
1

我真的懷疑你是否應該在這裏使用一個模板在所有。你的模板參數有一個非常清晰的接口,因此看起來應該只使用抽象基類。

你真的需要一個實例嗎?如果你確實需要改變對象的表現方式,這應該作爲一個單獨的練習來完成,而不是使用它的模板的一部分。

+0

Tx爲答案,我首先嚐試了OO方法,並使用模板解決了我遇到的設計問題(該OO無法從析構函數調用虛擬方法) – Ralf 2011-03-16 13:02:53

+0

也可能有解決方案,您只需需要確保函數在析構函數之前調用,而不是從函數調用。 – CashCow 2011-03-16 13:12:39

+0

在我的情況下,該方法必須在類的析構之前調用,因此我從析構函數中調用它,以確保我不想將模板類的用戶留給任何意義上的責任。 – Ralf 2011-03-17 06:30:15

2

您可以使用語法obj.f()obj->f()(基於obj的類型)編寫一個caller類模板,其作用是調用該函數。

下面是一個說明這種方法的一個小例子:

template<typename T> 
struct caller 
{ 
    static void call(T &obj) { obj.f(); } //uses obj.f() syntax 
}; 

template<typename T> 
struct caller<T*> 
{ 
    static void call(T* obj) { obj->f(); } //uses obj->f() syntax 
}; 

caller類模板使用該示例類:

template<typename T> 
struct X 
{ 
    T obj; 
    X(T o) : obj(o) {} 
    void h() 
    { 
     caller<T>::call(obj); //this selects the appropriate syntax! 
    } 
}; 

看到ideone這個在線運行演示:http://www.ideone.com/H18n7

-

編輯:

這甚至更通用。在這裏,你甚至可以通過你想撥打的功能caller。現在caller沒有被調用的函數硬編碼!

http://www.ideone.com/83H52

+0

不會需要在'caller'中重複'T'的接口嗎? – davka 2011-03-16 12:39:09

+0

@davka:查看編輯。 :-) – Nawaz 2011-03-16 12:42:01

+1

+1也很好,謝謝:) – Ralf 2011-03-17 06:51:42