3

我有一個關於多態性的問題,也許是其他技術。考慮以下方案:C++ - 多態:帶有派生類的容器類的初始化

#include <vector> 
#include <memory> 

using namespace std; 

struct Base 
{ }; 

struct Derived : Base 
{ }; 

class group 
{ 
    private: 
     vector< unique_ptr<Base> > V; 
    public: 
     group(/* what to put here? */) 
     : V(/* again: how to construct V? */) 
     { } 
} 

在這裏,我有三個類:一個基類,稱爲Base;派生類,稱爲Derived;和最後一堂課,group,這將作爲包裝來保存Base指針。但我想實現如下:

  • 使用移動語義。應該不涉及副本。調用者應該能夠給予構造幾個的臨時,作爲構造函數會偷他們:

    group my_wonderful_group(Base(/* parameters */) , Derived (/* ... */) , Derived (/* ... */)); 
    
  • 初始化V在初始化列表。這樣,V可能是const -qualified(儘管在初始化列表進行中初始化成員對象的所有其他好處)。

我已經試過幾件事情,但他們要麼似乎並不appropiate,或僅僅是概念上遠離自己的目標:

  • initializer_list S'元素不可移動; unique_ptr s是只可移動
  • 可變模板看起來不符合製作目標Vconst。由於initializer_list s不能容納不同類型的對象(就像vector s),我首先想到了它們,但是,怎麼樣?在閱讀了很多例子之後,我仍然不知道如何製作可變模板構造函數,或者即使沒有變通方法也可以這樣做(即:創建一些init()函數,該函數需要一個Base,一個Derived,...並玩它)。
  • 創建一個函數返回unique_ptr<Base>並將其參數轉發給構造函數Derived,從而充當group類的用戶友好包裝。然而,這並沒有什麼意義,正如我在下面附上的代碼所說明的那樣。

這是我有:

#include <string> 
#include <vector> 
#include <memory> 
#include <initializer_list> 
#include <iostream> 

using namespace std; 

struct Base 
{ 
    string s; 
    Base(Base && b) : s(move(b.s)) { } 
    Base(const string & S) : s(S) { } 
}; 

struct Derived : Base 
{ 
    Derived(const string & S) : Base(S) { } 
}; 

unique_ptr<Base> 
BASE (const string & S) 
{ 
    return unique_ptr<Base>(new Base(S)); 
} 

unique_ptr<Base> 
DERIVED (const string & S) 
{ 
    return unique_ptr<Base>(new Derived(S)); 
} 

class C 
{ 
    private: 
     vector< unique_ptr<Base> > V; 
    public: 
     template<typename ... T> 
      C 
      (T ... t) : V({ t... }) 
      { } 

     void 
      print 
      (void) 
      { 
       for (const auto & x : this->V) 
        cout << x->s << endl; 
      } 
      ; 
}; 


int main(void) 
{ 
    C c(BASE("hola") , DERIVED("der1") , DERIVED("bor3")); 
    c.print(); 
    return 0; 
} 

它抱怨,但是,有關的用法[刪除]複製被稱爲unique_ptr構造,當在group構造函數中的參數包擴大並逗號分隔放入initializer_list用於初始化V

我想我的問題歸結爲一個vector,實際上,可以應用於一個:vector<unique_ptr<Base>>(/* initialize with derived classes */)。我認爲之前必須解決這個問題,因爲多態是基本的C++,並且持有派生類的對象似乎是它的一個非常常見的用法。作爲說明,我使用g++ 4.8.1

在此先感謝。最好的問候, 卡里什

P.S .:我剛剛讀了this question這似乎部分覆蓋我的問題。

+0

你考慮使用Boost.Variant(www.boost.org/libs/variant)的?因爲它看起來確實與您想要通過小組班級達到的內容相匹配。 – lip

+0

請參閱http://stackoverflow.com/questions/8468774/can-i-list-initialize-a-vector-of-move-only-type來解決向量初始化。 – Jarod42

回答

2

以下應解決你所有的問題:

#include <iostream> 
#include <memory> 
#include <string> 
#include <vector> 

using namespace std; 

struct Base 
{ 
    string s; 
    Base(const Base& b) = delete; 
    Base(Base && b) : s(move(b.s)) { } 
    Base(const string & S) : s(S) { } 
}; 

struct Derived : Base 
{ 
    Derived(const string & S) : Base(S) { } 
}; 

#if 1 // not in C++11 
template <typename T, typename ... Ts> 
std::unique_ptr<T> make_unique(Ts&&...args) 
{ 
    return std::unique_ptr<T>(new T{std::forward<Ts>(args)...}); 
} 
#endif 

// vector<move_only> cannot be construct from initializer list :-/ 
// Use this work around 
template <typename Base, typename ... Ts> 
std::vector<std::unique_ptr<Base>> make_vector_of_unique(Ts&&... ts) 
{ 
    std::unique_ptr<Base> init[] = {make_unique<Ts>(std::forward<Ts>(ts))...}; 
    return std::vector<std::unique_ptr<Base>> { 
     std::make_move_iterator(std::begin(init)), 
     std::make_move_iterator(std::end(init))}; 
} 

class C 
{ 
private: 
    const std::vector< std::unique_ptr<Base> > V; 
public: 
    template<typename ... Ts> 
    C(Ts&& ... t) : V(make_vector_of_unique<Base>(std::forward<Ts>(t)...)) 
    {} 

    void print() 
    { 
     for (const auto & x : this->V) 
      std::cout << x->s << std::endl; 
    } 
}; 

int main() { 
    C c(Base("hola") , Derived("der1") , Derived("bor3")); 
    c.print(); 
    return 0; 
} 
+0

完美!謝謝! – Kalrish