2016-09-10 69 views
3

假設我有一個基類foo和兩個派生類AB。然後,我有另一個類bar,它有一個數據成員xyz,其可以是A,或,但類型的依賴於其他數據成員x_typey_type,和z_type,這些值不可用在編譯時。我雖然關於使用模板數據成員和在構造函數中定義類型,我得到的類型的值,但顯然至少在C++ 11中是不可能的。那麼如何繼續?基於另一個數據成員的類數據成員類型?

class foo{ 
public: 
    foo(double); 
    int x_type; 
    virtual double do_something(double, int) = 0; 
}; 

class A: public foo { 
public: 
    A(double, double); 
    double do_something(double, int); 

private: 
    double z1; 
    double z2; 
}; 

class B: public foo { 
public: 
    B(double); 
    double do_something(double, int); 

private: 
    double w; 
}; 

class bar { 
public: 
    bar(); 
    double do_something2(int); 

private: 
    int x_type; 
    int y_type; 
    int x_type; 
    x; // these are either A or B... 
    y; 
    z; 
}; 

而且在構造函數中我會像

if(x_type == 1){ 
    x = A(arg1, arg2); 
} else { 
    x = B(arg3); 
} 

在我的實際應用中可以有更高的許多派生類和未知類型的數據成員。我想知道是否有可能使bar具有多個模板參數的模板類,但我不確定這是可能的,因爲參數類型取決於另一個參數?

+0

通過整數值枚舉類型是Evil™反模式。可能這對應於帶有指向多態類「foo」的設計。其中應該有一個虛擬析構函數。 –

回答

2

您需要使用多態,並採取共同的基類美孚的優勢:

if(x_type == 1){ 
    x.reset(new A(arg1, arg2)); 
} else { 
    x.reset(new B(arg3)); 
} 

這是一個很好:

private: 
    int x_type; 
    int y_type; 
    int x_type; 
    std::unique_ptr<Foo> x; // these are either A or B... 
    std::unique_ptr<Foo> y; 
    std::unique_ptr<Foo> z; 

}; 

然後在你的構造函數,你可以從正確的類型創建XYZ練習將創建正確的Foo實例的代碼移動到所謂的「工廠」類或函數中,以隱藏決策制定邏輯和構造細節(有時可能相當複雜)。

1

所有變量的靜態類型必須在編譯時已知,因此它不能根據運行時對象的值進行更改。使這項工作的方式是讓xy,並且z都有型std::uniqe_ptr<foo>,然後動態地分配在運行時AB對象:

class bar { 
public: 
    bar(some_type something) { 
     if (something == some_value) { 
      b.x = new A(3.14, 12.34); 
     } else { 
      b.x = new B(456.78); 
     } 
    } 
private: 
    int x_type; 
    std::unique_ptr<foo> x; 
    //... 
}; 

int main() { 
    bar b(whatever()); 
} 

在這種情況下,你也應該申報foo::~foo()是虛擬的,以便確保派生的對象被正確銷燬。

這也可能是一個好主意™,它可以完全消除x_type和朋友,並編寫代碼,在創建後不會關心x的實際類型。

1

我想知道是否有可能使欄模板類與多個模板參數,但我不知道這是可能的,因爲參數類型取決於另一個參數嗎?

我不知道這是否有幫助,但我會讓它在這裏以防萬一。

您會發現,模板的不同特化可以從不同的類繼承。所以,你可以有:

// fwd decl 
template <int kind> class bar; 

template <> class bar<1> : public A { 
public: 
    bar(double x, double y) : A(x,y) { } 
}; 

template <> class bar<2> : public B { 
public: 
    bar(double a) : B(a) { } 
}; 

在後一階段,當你來到一個class C : public foo,你只需指定另一個kind到一個新的bar模板專門有你有它:使用bar作爲統一(警告......但一個統一的類型 - 不低於普通foo祖先bar<1>bar<2>將是兩種不同類型除外)

所以,好吧,如果你不想繼承,你可以擁有它DIF不同has-a特定bar模板專業化。

template <int kind> class bar; 

template <> class bar<1> { 
    A val; 
public: 
    bar(double x, double y) : val(x,y) { } 

    void doSomething2(...) { 
    // use the val of type A 
    } 
}; 
template <> class bar<2> { 
    B val; 
    double y_; 
public: 
    bar(double x, double y) : val(x), y_(y) { } 

    void doSomething2(...) { 
    // use the val of type B and a separate y_ 
    } 
}; 
1

我雖然有關使用模板數據成員和 構造,在那裏我得到的值類型,但顯然這 是不可能的,至少用C定義類型++ 11

C++ 11通過使用make_*模板函數創建適當類型的附帶處理取決於一些參數模板結構的標準架構目的。見例如make_tuple功能:

template <class T> 
struct foo { 
    T t; 
    foo(T t): t(t) { } 
}; 

template <class T> 
foo<T> make_foo(T t) { return foo<T>(t); } 
2

如果能夠用於xyz所有類型都從派生的所有

auto t = std::make_tuple(1, "abc", 1.0); 
// decltype(t) == std::tuple<int, char const*, double> 

這可以通過創建模板類和建築模板功能來實現共同基類,基指針解決方案,std::unique_ptr(Lyubomir Stankov +1)是(恕我直言)一個很好的解決方案。

但你問「是否有可能使欄具有多個模板參數的模板類」。

是的:這是可能的。不是很優雅(恕我直言),但可能。

我建議爲了好玩以下解決方案,但我認爲,在更一般的情況下(注意,在我的例子,AB是不相關的類,從foo沒有更多的派生),可能是有用的(我希望如此)

#include <tuple> 
#include <string> 
#include <utility> 

class A 
{ 
    private: 
     double  d; 
     std::string s; 

    public: 
     A (double d0, std::string s0) : d { d0 }, s { s0 } { } 
}; 

class B 
{ 
    private: 
     long l; 

    public: 
     B (long l0) : l { l0 } { } 
}; 

template <typename Tx, typename Ty, typename Tz> 
class bar 
{ 
    private: 

     template <typename ... Ts> 
     using tpl = std::tuple<Ts...>; 

     template <std::size_t ... Is> 
     using is = std::index_sequence<Is...> const; 

     template <std::size_t N> 
     using mis = std::make_index_sequence<N>; 

     Tx x; 
     Ty y; 
     Tz z; 

     template <typename ... Tsx, std::size_t ... Isx, 
       typename ... Tsy, std::size_t ... Isy, 
       typename ... Tsz, std::size_t ... Isz> 
     bar (tpl<Tsx...> const & tx0, is<Isx...> const &, 
      tpl<Tsy...> const & ty0, is<Isy...> const &, 
      tpl<Tsz...> const & tz0, is<Isz...> const &) 
     : x { std::get<Isx>(tx0) ... }, 
      y { std::get<Isy>(ty0) ... }, 
      z { std::get<Isz>(tz0) ... } 
     { } 

    public: 

     template <typename ... Tsx, typename ... Tsy, typename ... Tsz> 
     bar (tpl<Tsx...> const & tx0, 
      tpl<Tsy...> const & ty0, 
      tpl<Tsz...> const & tz0) 
     : bar(tx0, mis<sizeof...(Tsx)> {}, 
       ty0, mis<sizeof...(Tsy)> {}, 
       tz0, mis<sizeof...(Tsz)> {}) 
     { } 
}; 

int main() 
{ 
    bar<A, B, A> aba{ std::make_tuple(2.3, "str1"), 
         std::make_tuple(4), 
         std::make_tuple(5.4, "str2") }; 

    bar<B, A, B> bab{ std::make_tuple(3), 
         std::make_tuple(3.2, "str3"), 
         std::make_tuple(5) }; 
} 

不幸的是該實施例中使用std::make_index_sequencestd::index_sequence和是C++ 14層的功能。

如果你想實現C++ 11 foo,則可以實現以下結構struct indexSeqstruct indexSeqHelper,替代std::index_sequencestd::make_index_sequence

template <std::size_t ...> 
struct indexSeq 
{ }; 

template <std::size_t N, std::size_t ... Next> 
struct indexSeqHelper 
{ using type = typename indexSeqHelper<N-1U, N-1U, Next ... >::type; }; 

template <std::size_t ... Next > 
struct indexSeqHelper<0U, Next ... > 
{ using type = indexSeq<Next ... >; }; 

,並定義ismis如下

template <std::size_t ... Is> 
    using is = indexSeq<Is...>; 

    template <std::size_t N> 
    using mis = typename indexSeqHelper<N>::type; 
相關問題