2016-03-19 29 views
5

Hãy xem xét các tree lớp sauLàm cách nào để sử dụng `std :: array` cho tham số mẫu của biểu mẫu` mẫu <typename> lớp`?

template<typename T, template<typename> class Tuple> 
class tree 
{ 
private: 
    T m_value; 
    Tuple<tree> m_children; 
}; 

template<typename T, std::size_t N> 
using static_tree = tree<T, std::array<T, N>>; 

mà không được xác định rõ. std::array<T, N> không phải là thông số mẫu phù hợp cho Tuple. Tôi cho rằng ý định của static_tree là rõ ràng. Chúng tôi có thể làm điều gì đó như

template<std::size_t N> 
struct helper 
{ 
    template<typename T> 
    using type = std::array<T, N>; 
}; 

template<typename T, std::size_t N> 
using static_tree = tree<T, helper<N>::template type>; 

Có tùy chọn nào khác không có lớp học helper không?

+0

Nếu bạn sở hữu 'tree', tại sao không làm cho' Tuple' một tham số kiểu chứ không phải là một bản mẫu tham số? –

+0

@AlanStokes Làm cách nào để xác định 'static_tree' trong trường hợp đó? Đó sẽ là một sự đệ quy vô tận, phải không? – 0xbadf00d

+0

Thực tế là 'std :: array' cần tham số nguyên. Không có cách nào xung quanh chỉ định nó * đâu đó *. Đối với nơi này, có rất nhiều lựa chọn thay thế, và của bạn không xa rõ ràng. – davidhigh

Trả lời

1

Tôi nghĩ câu hỏi của bạn có chứa một vấn đề cơ bản khác với những gì bạn đã yêu cầu một cách rõ ràng (nó đã ném tôi ra một chút trong lần lặp lại trước của câu trả lời này). Kết hợp các phần khác nhau của câu hỏi của bạn, về cơ bản bạn đang cố gắng để nhanh chóng một số lớp cây có một thành viên đó là một std::array của cùng một lớp là tốt. Điều này rõ ràng là không thể. Bạn có thể muốn cây phải giữ một số Tuple của con trỏ (thông minh hoặc cách khác).

Một cách để làm như vậy sẽ sử dụng lớp helper của bạn, nhưng việc sửa đổi lớp để

template<typename T, template<typename> class Tuple> 
class tree 
{ 
    // Indirection (I'm omitting the question of whether these should be 
    //  smart pointers. 
    Tuple<tree<T, Tuple> *> m_children; 
}; 

Một cách khác nhau sẽ làm cho Tuple một tham số mẫu thông thường, như sau:

#include <array> 
#include <type_traits> 

template<typename T, class Tuple> 
class tree                                 
{ 
private: 
    static_assert(
     std::is_same<void *, typename Tuple::value_type>::value, 
     "Tuple must be a container of void *"); 

private: 
    T m_value; 
    Tuple m_children; 
}; 

template<typename T, std::size_t N> 
using static_tree = tree<T, std::array<void *, N>>; 

int main() 
{ 
    static_tree<int, 8> t; 
} 

Một mặt, lớp trợ giúp đã bị loại bỏ. OTOH, Tuple là một thùng chứa void *: các instantiators nhận thức được điều này, và lớp bên trong cần thực hiện các phôi. Đó là một sự cân bằng. Tôi sẽ dính vào phiên bản gốc của bạn (với các sửa đổi được đề xuất, tất nhiên).

+1

Bạn biết rằng với định nghĩa của bạn 'static_tree' là * không * một cái cây, phải không? Con cái của cây cũng cần phải là cây. – 0xbadf00d

+0

@ 0xbadf00d Bạn nói đúng, nhưng khi suy nghĩ, tôi nghĩ rằng đó là do một số vấn đề trong câu hỏi của bạn gây ra. Xem cập nhật. –

4

Thay vì có chức năng trợ giúp là ngoại lệ cho std::array, tôi muốn đề xuất đó là quy tắc. Thay vì lấy tham số mẫu mẫu, hãy tham số lớp giao tiếp thông số. bản Lập trình meta là dễ dàng hơn nhiều khi mà mọi thứ ở khắp mọi nơi là một loại (tránh template mẫu và lập luận phi-type):

template<typename T, typename TupleMfn> 
class tree 
{ 
private: 
    using Tuple = TupleMfn::template apply<tree>; 

    T m_value; 
    Tuple m_children; 
}; 

với:

template <size_t N> 
struct array_builder { 
    template <class T> 
    using apply = std::array<T, N>; 
}; 

template <typename T, size_t N> 
using static_tree = tree<T, array_builder<N>>; 

Điều này sẽ làm cho nó dễ dàng hơn để sử dụng với các loại khác container là tốt, vì chúng ta có thể làm cho một wrapper cho mẫu mẫu mà cho chúng ta trở lại một metafunction:

template <template <typename...> class X> 
struct as_metafunction { 
    template <class... Args> 
    using apply = X<Args...>; 
}; 

template <typename T> 
using vector_tree = tree<T, as_metafunction<std::vector>>; 

Nếu bạn đang cảm thấy đặc biệt nóng nảy, bạn có thể cung cấp một phiên bản Lập trình meta-thân thiện của std::array:

template <class T, class N> 
struct meta_array : std::array<T, N::value> // obviously I am terrible at naming things 
{ }; 

template <size_t N> 
using size_t_ = std::integral_constant<size_t, N>; 

Và sau đó cung cấp args giữ chỗ cho tree to apply:

template <class T, size_t N> 
using static_tree = tree<T, meta_array<_, size_t_<N>>>; 

template <class T> 
using vector_tree = tree<T, std::vector<_>>; 
+0

Cây 'của bạn là * không * một cái cây, vì' m_children' là một bộ của 'T'. – 0xbadf00d

+0

@ 0xbadf00d Typo – Barry

0

Một lớp X không thể chứa nhiều bản sao của các phiên bản thực tế của lớp X, ngoại trừ một cách hợp lý.

Một nếu chúng ta có

struct X { 
    std::array<X, 2> data; 
}; 

kích thước chỉ nhất có thể cho X là vô cực, như sizeof(X) = 2*sizeof(X) và tất cả các loại bằng C++ có sizeof(X)>=1.

C++ không hỗ trợ các loại vô hạn lớn.

Vấn đề thứ hai của bạn là trường hợp loại không phải là mẫu.

template<typename T, template<typename> class Tuple> 
class tree 

này có một loại TtemplateTuple. Đối số thứ hai không phải là loại.

template<typename T, std::size_t N> 
using static_tree = tree<T, std::array<T, N>>; 

đây, lập luận thứ hai của bạn là một loại, không phải là một mẫu.

template<std::size_t N> 
struct array_of_size { 
    template<class T> 
    using result=std::array<T,N>; 
}; 
template<typename T, std::size_t N> 
using static_tree = tree<T, array_of_size<N>::template result>; 

sẽ, ngoài "vấn đề kích thước vô hạn" ở trên, giải quyết vấn đề của bạn. Ở đây chúng tôi đang chuyển mẫu array_of_size<N>::result đến tree.

Để giải quyết vấn đề kích thước vô hạn, bạn phải con trỏ lưu trữ (hoặc thứ gì đó tương đương) trong mảng. Vì vậy, chúng tôi nhận được:

template<std::size_t N> 
struct array_of_ups_of_size { 
    template<class T> 
    using result=std::array<std::unique_ptr<T>,N>; 
}; 
template<typename T, std::size_t N> 
using static_tree = tree<T, array_of_ups_of_size<N>::template result>; 

và bây giờ static_tree của bạn có N trẻ em, mỗi trong số đó là một unique_ptr đến một tương tự static_tree.

Điều này vẫn không hoạt động, do các vấn đề về phá hủy.

template<typename T, template<typename> class Tuple> 
class tree 
{ 
private: 
    T m_value; 
    Tuple<tree> m_children; 
public: 
    ~tree(); 
}; 

template<typename T, template<typename> class Tuple> 
tree<T,Tuple>::~tree() = default; 

Tôi nghĩ rằng các sửa lỗi ở trên, lạ như có thể.

Về cơ bản, khi bạn tạo mảng trẻ em, loại cây chưa hoàn thành. Tại hủy diệt, xóa được gọi. Tại thời điểm đó, cây phải được hoàn thành. Bằng cách trì hoãn các dtor, chúng tôi hy vọng đối phó với vấn đề.

Tôi không chắc chắn kỹ thuật này có bắt buộc đối với các mẫu không, nhưng nó là dành cho các lớp không phải mẫu.

0

Hoặc bạn có thể thực hiện một bản mẫu tham số ràng buộc như bạn đề nghị (hơi tổng quát hơn của bạn helper):

template<std::size_t N, template<typename,std::size_t> class T> 
struct bind2nd 
{ 
    template<typename F> 
    using type = T<F,N>; 
}; 

template<std::size_t N, typename T> 
using static_tree = tree<T, bind2nd<N,std::array>::template type>; 
Các vấn đề liên quan