2011-02-10 33 views
5

Các mã sau đây đại diện cho một container dựa trên std :: vectorTemplates và STL

template <typename Item> 
struct TList 
{ 
    typedef std::vector <Item> Type; 
}; 


template <typename Item> 
class List 
{ 
private 
      typename TList <Item>::Type items; 
    .... 
} 

int main() 
{ 
    List <Object> list; 
} 

Có thể templatize std :: vector và tạo một container chung, một cái gì đó như thế?

template <typename Item, typename stl_container> 
struct TList 
{ 
    typedef stl_container<Item>; 
}; 

nơi stl_container đại diện tiêu chuẩn :: vector, std :: list, std :: set ...? Tôi muốn chọn loại container tại thời điểm tạo ra.

List <Object, std::vector> list; //vector of objects, not a real code 
List <Object, std::vector> list; //list of objects, not a real code 

Cám ơn câu trả lời của bạn ...

câu hỏi Cập nhật:

Tôi đã thử các mã sau nhưng có những sai sót:

#include <vector> 
template <typename Item, typename Container> 
struct TList 
{ 
    typedef typename Container <Item>::type type; //Error C2059: syntax error : '<', Error C2238: unexpected token(s) preceding '; 
}; 


template <typename T> 
struct vector_container 
{ 
    typedef std::vector<T> type; 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
TList <int, vector_container> v; 
TList <int, map_container> m; 
} 
+0

Ai sẽ sử dụng lớp này? Tại sao 'List' không thể sử dụng' typedef vector/list/set items'? Điều gì sẽ là mục đích của một lớp học, được đưa ra một loại container và một loại giá trị, chỉ cần đặt cả hai với nhau? – UncleBens

+0

Re: chỉnh sửa, vẫn không thấy lý do tại sao bạn không thể viết danh sách 'List >;' etc – UncleBens

Trả lời

5

Có và không.

Bạn có thể sử dụng một tham số template mẫu, ví dụ,

template <typename Item, template <typename> class Container> 
struct TList { /* ... */ }; 

Tuy nhiên, nói chung, các thông số template mẫu không phải là đặc biệt hữu ích bởi vì số lượng và chủng loại các thông số mẫu phải phù hợp. Vì vậy, ở trên sẽ không khớp với std::vector bởi vì nó thực sự có hai tham số mẫu: một cho loại giá trị và một cho phân bổ. Thông số mẫu mẫu không thể tận dụng bất kỳ đối số mẫu mặc định nào.

Để có thể sử dụng std::vector mẫu như một cuộc tranh cãi, TList sẽ phải được khai báo là:

template <typename Item, template <typename, typename> class Container> 
struct TList { /* ... */ }; 

Tuy nhiên, với mẫu này, bạn sẽ không thể sử dụng std::map mẫu như một đối số vì nó có thông số mẫu bốn: loại khóa và giá trị, loại cấp phát và loại so sánh.

Thông thường, việc tránh các thông số mẫu của mẫu dễ dàng hơn nhiều vì tính không linh hoạt này.

+0

Bạn có thể thay thế 'template class Container' chỉ với' typename Container' và truy cập loại chứa trong vùng chứa bằng cách sử dụng 'Container :: value_type'? –

+0

@Billy: Chắc chắn; mỗi vùng chứa có một 'typ_type' typedef. Tuy nhiên, điều đó sẽ không cho phép bạn _create_ container, đó là những gì OP đang hỏi về. –

+0

@ James: Ah - tôi hiểu rồi. Dường như OP cần một số trình vòng lặp rồi :) –

0

Có thể templatize std :: vector và tạo vùng chứa chung, một cái gì đó tương tự không?

No. Bạn sẽ phải templatize chức năng hoặc đối tượng bằng cách sử dụng vùng chứa - bạn không thể templatize chính vùng chứa đó.

Ví dụ: xem xét một điển hình std::find:

template<class InputIterator, class T> 
InputIterator find (InputIterator first, InputIterator last, const T& value) 
{ 
    for (;first!=last; first++) if (*first==value) break; 
    return first; 
} 

Điều này làm việc cho bất kỳ vùng chứa nào, nhưng không cần tempalte với container. Ngoài ra, với điều đó, có vẻ như những gì bạn đang cố gắng làm là tạo mã độc lập cho vùng chứa, bạn có thể muốn mua hoặc mượn bản sao Scott Meyers' Effective STL và đọc Mục 2: Hãy coi chừng ảo giác về mã độc lập của vùng chứa.

+0

Không phải là các thuật toán sử dụng trình vòng lặp, thay vì vùng chứa/phạm vi một phần là nguyên nhân của việc không thể sử dụng mã độc lập của vùng chứa? Nó * là * có thể viết một chức năng tìm kiếm chung, mà cũng hoạt động hiệu quả cho thiết lập và bản đồ, chỉ không có chữ ký này. – UncleBens

+0

@UncleBens: Có - mục trong STL hiệu quả đang ngăn cản một người nào đó cố gắng chỉ sử dụng các tính năng mà tất cả các vùng chứa STL cung cấp, để họ có thể chuyển đổi chúng. (Meyers là tốt hơn tại giải thích hơn tôi) Nếu bạn có thể templatize một thuật toán bằng phương tiện của một mẫu sau đó bằng mọi cách đi cho nó.(Nhưng có thể bạn sẽ cần một vài chuyên môn về mẫu nếu bạn muốn nó có hiệu quả nhất cho cả hai vùng chứa chuỗi và liên kết;)) –

+0

Về khoản 2: Hãy coi chừng ảo tưởng về mã độc lập của vùng chứa, không phải hầu hết các thuật toán trong STL dưới tinh thần đó? – kirakun

2

tốt, bạn có thể hack nó lên với một vĩ mô:

template <typename T, typename stl_container = std::vector<T> > 
struct TList 
{ 
    typedef stl_container Type; 
}; 

#define TLIST(T, C) TList<T, C<T> > 

TList<int> foo; 
TList<int, std::list<int> > bar; 
TLIST(int, std::list) baz; 
+0

Tại sao bạn muốn giết rất nhiều con chó? : P –

+0

Cuz Tôi là người yêu mèo ;-). Nhưng yeah, đây là một hack. – Tim

+1

Tôi không biết nên upvote hoặc downvote. +1. –

11

Có, nhưng không trực tiếp:

template <typename Item, template <typename> class Container> 
struct TList 
{ 
    typedef typename Container<Item>::type type; 
}; 

Sau đó, bạn có thể xác định chính sách container khác nhau:

template <typename T> 
struct vector_container 
{ 
    typedef std::vector<T> type; 
}; 

template <typename T> 
struct map_container 
{ 
    typedef std::map<T, std::string> type; 
}; 

TList<int, vector_container> v; 
TList<int, map_container> m; 

Mặc dù có một chút chi tiết. * Để thực hiện mọi việc trực tiếp, bạn cần phải thực hiện the route described by James, nhưng khi ghi chú điều này là cuối cùng rất không linh hoạt.

Tuy nhiên, với C++ 0x chúng ta có thể làm điều này chỉ tốt:

#include <map> 
#include <vector> 

template <typename Item, 
      template <typename...> class Container, typename... Args> 
struct TList 
{ 
    // Args lets the user specify additional explicit template arguments 
    Container<Item, Args...> storage; 
}; 

int main() 
{ 
    TList<int, std::vector> v; 
    TList<int, std::map, float> m; 
} 

Perfect. Thật không may là không có cách nào để tái tạo điều này trong C++ 03, ngoại trừ thông qua các lớp chính sách gián tiếp giới thiệu như mô tả ở trên.


* Tôi muốn nhấn mạnh rằng "Chi tiết một chút" có nghĩa là "điều này không chính thống". Giải pháp chính xác cho vấn đề của bạn là thư viện chuẩn, as Jerry explains. Bạn chỉ cho phép người dùng adapter container của bạn chỉ định toàn bộ container loại trực tiếp:

template <typename Item, typename Container = std::vector<Item>> 
struct TList 
{}; 

Nhưng vẫn để lại một vấn đề lớn: những gì nếu tôi không muốn kiểu giá trị của các container để được Item nhưng something_else<Item>? Nói cách khác, làm cách nào tôi có thể thay đổi loại giá trị của vùng chứa hiện tại thành vùng chứa khác? Trong trường hợp của bạn, bạn không nên đọc thêm, nhưng trong trường hợp chúng tôi làm, chúng tôi muốn rebind một vùng chứa.

Thật không may cho chúng tôi, các container không có chức năng này, mặc dù allocators làm:

template <typename T> 
struct allocator 
{ 
    template <typename U> 
    struct rebind 
    { 
     typedef allocator<U> type; 
    }; 

    // ... 
}; 

Điều này cho phép chúng tôi để có được một allocator<U> đưa ra một allocator<T>. Làm thế nào chúng ta có thể làm tương tự cho các thùng chứa mà không có tiện ích xâm nhập này? Trong C++ 0x, thật dễ dàng:

template <typename T, typename Container> 
struct rebind; // not defined 

template <typename T, typename Container, typename... Args> 
struct rebind<T, Container<Args...>> 
{ 
    // assumes the rest are filled with defaults** 
    typedef Container<T> type; 
}; 

Với std::vector<int>, chúng tôi có thể thực hiện rebind<float, std::vector<int>>::type, ví dụ. Không giống như C++ 0x trước giải pháp, chương trình này có thể được mô phỏng trong C++ 03 với các macro và lặp ..


** Chú ý cơ chế này có thể được thực hiện mạnh mẽ hơn nhiều, giống như đó nêu rõ lý lẽ để giữ , mà để rebind, mà để rebind mình trước khi sử dụng như là đối số, vv, nhưng đó là trái như là một tập thể dục cho người đọc. :)

+1

Ý tưởng hay. Có bất kỳ vấn đề nào không thể giải quyết được với một lớp bổ sung không? –

+0

@ James: Không. Tôi thậm chí có thể nhìn thấy trán của tôi nếu tôi sử dụng một tấm gương. – GManNickG

+0

Lol - gọn gàng. Và các bình luận rất vui nhộn. +1. –

6

Tôi hơi bối rối vì sao một số người rất thông minh (và có thẩm quyền) đang nói không.

Trừ khi tôi đã đọc sai câu hỏi của bạn, những gì bạn đang cố gắng hoàn thành hầu như giống với "bộ điều hợp vùng chứa" trong thư viện chuẩn. Mỗi giao diện cung cấp giao diện cho một số loại vùng chứa cơ bản, với loại vùng chứa sẽ được sử dụng như một tham số mẫu (có giá trị mặc định).

Ví dụ: std::stack sử dụng một số vùng chứa khác (ví dụ:, std::deque, std::list hoặc std::vector) để giữ các đối tượng và bản thân std::stack chỉ cung cấp giao diện đơn giản/bị hạn chế khi bạn chỉ muốn sử dụng các hoạt động ngăn xếp. Vùng chứa bên dưới sẽ được sử dụng bởi std::stack được cung cấp dưới dạng tham số mẫu. Dưới đây là cách mã trông trong tiêu chuẩn:

namespace std { 
    template <class T, class Container = deque<T> > 
    class stack { 
    public: 
     typedef typename Container::value_type value_type; 
     typedef typename Container::size_type size_type; 
     typedef Container      container_type; 
    protected: 
     Container c; 
    public: 
     explicit stack(const Container& = Container()); 
     bool empty() const    { return c.empty(); } 
     size_type size() const   { return c.size(); } 
     value_type& top()    { return c.back(); } 
     const value_type& top() const { return c.back(); } 
     void push(const value_type& x) { c.push_back(x); } 
     void pop()      { c.pop_back(); } 
    }; 
} 

Tất nhiên, có lẽ tôi vừa hiểu lầm câu hỏi - nếu như vậy, tôi xin lỗi trước cho những người mà tôi (loại) không đồng ý.

+0

Jerry: OP muốn mã được viết là 'MyClass ' để có thể tạo 'std :: vector '. Trong ví dụ 'std :: stack', tên được truyền không phải là' std :: deque', nó là 'std :: deque ' - không phải là một mẫu lớp, mà là một lớp mẫu. Tức là, mã của bạn sẽ dẫn đến OP phải làm 'MyClass >'. –

+0

@Billy O'Neal: Tôi đã đọc lại câu hỏi và vẫn không thể tìm thấy nơi anh ấy nói đó là điều anh ấy muốn. Có lẽ tôi đã không ngủ đủ đêm qua hay một cái gì đó mặc dù .... –

+0

Tôi nghĩ rằng điểm của câu hỏi là để có được các container mà không đi qua toàn bộ loại của nó vào tham số mẫu. (Ví dụ, vì vậy tôi có thể tạo kiểu giá trị 'secret_type '.) Nhưng tôi nghĩ câu trả lời của bạn cũng quan trọng vì chúng ta nên giải quyết vấn đề, chứ không chỉ trả lời câu hỏi, và đây là cách mà hầu hết chúng ta sẽ nói về nó. – GManNickG

-1

Bạn có thể sử dụng các thông số mẫu mẫu như những người khác đã đề cập ở đây. Khó khăn chính với điều này không phải là các loại container khác nhau có các tham số mẫu không giống nhau, nhưng tiêu chuẩn cho phép các thùng chứa tiêu chuẩn, như vector, có các tham số mẫu ngoài các tài liệu cần thiết.

Bạn có thể khắc phục điều này bằng cách cung cấp các loại lớp con của riêng bạn mà chấp nhận các thông số mẫu thích hợp và cho phép bất kỳ tính năng bổ sung (trong đó phải có giá trị mặc định) được lấp đầy trong việc thực hiện của tôi:

template < typename T > struct simple_vector : std::vector<T> {}; 

Hoặc bạn có thể sử dụng biểu mẫu typedated typedef:

template < typename T > struct retrieve_vector { typedef std::vector<T> type; }; 
+3

[Tiêu chuẩn C++ không cho phép triển khai có thêm tham số mẫu tùy chọn.] (Http://stackoverflow.com/questions/1469743/standard-library-containers-with-additional-optional-template-parameters) –

+0

I downvote vì bạn thừa kế từ các thùng chứa tiêu chuẩn. Chúng không có một destructor ảo, vì vậy sẽ rất nguy hiểm khi sử dụng lớp 'simple_vector ' ở những nơi mà bạn mong đợi một 'vector '; người dùng của lớp có thể (và sẽ) làm cho nó sai và để cho 'vector :: ~ vector' được gọi thay vì' simple_vector :: ~ simple_vector'. –

+0

Không phải trong trường hợp này Alexandre. Hãy dành một chút thời gian để suy nghĩ về điều đó. Hoặc lâu hơn ... –