2013-04-01 28 views
7

Mục tiêu của tôi là để làm một cái gì đó để ví dụ,mẫu variadic: sản xuất một tuple các cặp của các nguyên tố lân cận

pairs<1,2,3,4>() 

Có kiểu trả về

std::tuple<some_other_type<1,2>, some_other_type<2,3>, some_other_type<3,4>> 

tôi tự hỏi nếu điều này thậm chí có thể với lập trình meta mẫu C++, và làm thế nào nó có thể được thực hiện. Để thực sự tạo ra giá trị, có vẻ như tôi có thể sử dụng tuple_cat để đệ quy nối với đầu ra, nhưng tôi thấy khó thể hiện kiểu trả về, vì nó là bản thân hàm variadic và hiệu quả của số tham số mẫu. Làm phức tạp tình hình, nếu tôi đã đi tuyến tuple_cat nó có vẻ như tôi cũng sẽ phải quá tải các chức năng để có một tuple để nối vào, và nối sẽ xảy ra trong thời gian chạy, không biên dịch-thời gian. Tôi có đang theo đuổi một con ngỗng hoang dã ở đây không?

+0

+1 Đây có lẽ là có thể đạt được bằng cách sử dụng lớp ... –

Trả lời

12

Đây là cách để thực hiện điều đó. Với lớp học của bạn mẫu some_other_type:

template<int I, int J> 
struct some_other_type { }; 

Và đưa ra một số máy móc thiết bị ẩn trong detail namespace:

namespace detail 
{ 
    template<int... Is> 
    struct pairs { }; 

    template<int I, int J> 
    struct pairs<I, J> 
    { 
     using type = std::tuple<some_other_type<I, J>>; 
    }; 

    template<int I, int J, int... Is> 
    struct pairs<I, J, Is...> 
    { 
     using type = decltype(std::tuple_cat(
       std::tuple<some_other_type<I, J>>(), 
       typename pairs<J, Is...>::type())); 
    }; 
} 

Bạn có thể cung cấp một chức năng đơn giản mà instantiates mẫu lớp helper:

template<int... Is> 
typename detail::pairs<Is...>::type pairs() 
{ 
    return typename detail::pairs<Is...>::type(); 
} 

Và đây là cách bạn sẽ sử dụng nó (và một trường hợp thử nghiệm):

#include <type_traits> 

int main() 
{ 
    auto p = pairs<1, 2, 3, 4>(); 

    // Won't fire! 
    static_assert(
     std::is_same< 
      decltype(p), 
      std::tuple< 
       some_other_type<1,2>, 
       some_other_type<2,3>, 
       some_other_type<3,4>> 
      >::value, 
      "Error!"); 
} 

Cuối cùng, đây là live example.


CẢI THIỆN: (tại sao viết <1, 2, 3, 4> khi người ta có thể viết <1, 5>)?

Cũng có thể mở rộng giải pháp trên để không cần phải viết thủ công mọi số giữa giá trị tối thiểu và tối đa làm đối số mẫu của pairs(). Với máy móc thiết bị bổ sung bên dưới, một lần nữa ẩn trong một namespace detail:

namespace detail 
{ 
    template <int... Is> 
    struct index_list { }; 

    template <int MIN, int N, int... Is> 
    struct range_builder; 

    template <int MIN, int... Is> 
    struct range_builder<MIN, MIN, Is...> 
    { 
     typedef index_list<Is...> type; 
    }; 

    template <int MIN, int N, int... Is> 
    struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...> 
    { }; 

    // Meta-function that returns a [MIN, MAX) index range 
    template<int MIN, int MAX> 
    using index_range = typename range_builder<MIN, MAX>::type; 

    template<int... Is> 
    auto pairs_range(index_list<Is...>) -> decltype(::pairs<Is...>()) 
    { 
     return ::pairs<Is...>(); 
    } 
} 

Có thể định nghĩa một hàm helper pairs_range() mà chấp nhận 2 đối số mẫu xác định phạm vi [begin, end) - nơi end không được bao gồm, trong phong cách của Thư viện Tiêu chuẩn:

template<int I, int J> 
auto pairs_range() -> decltype(pairs_range(detail::index_range<I, J>())) 
{ 
    return pairs_range(detail::index_range<I, J>()); 
} 

Và đây là cách người ta sẽ sử dụng nó (trong đó có một trường hợp thử nghiệm):

int main() 
{ 
    // Won't fire! 
    static_assert(
     std::is_same< 
      decltype(pairs_range<1, 5>()), 
      decltype(pairs<1, 2, 3, 4>()) 
      >::value, 
      "Error!"); 
} 

Và một lần nữa, đây là live example.

4

Đây là phiên bản của tôi về nó (live here), 100% thời gian biên dịch, trở về danh sách tham số mới như một loại (không phải là một chức năng cửa sổ mới)

Trước tiên, hãy xác định cấu trúc kết quả của chúng tôi:

template<int a, int b> 
struct tpair 
{ 
}; 

template<typename... p> 
struct final_ 
{ 
}; 

Điểm mấu chốt là gói tham số concat. Đây là cấu trúc sẽ thực hiện công việc:

template<typename a, typename b> 
struct concat 
{ 
}; 

template<typename a, typename... b> 
struct concat<a, final<b...>> 
{ 
    typedef final_<a,b...> type; 
}; 

Bây giờ, cấu trúc được sử dụng để 'pairize' danh sách của bạn. Thông thường nó sẽ thất bại với số lẻ của các giá trị:

template<int a, int b, int... values> 
struct pairize 
{ 
    // Choose one of the following versions: 
    // First version: only non-overlapping pairs : (1,2) (3,4) ... 
    typedef typename concat<tpair<a,b>, typename pairize<values...>::type>::type type; 
    // Second version: overlapping pairs : (1,2) (2,3) (3,4)... 
    typedef typename concat<tpair<a,b>, typename pairize<b,values...>::type>::type type; 
}; 

template<int a, int b> 
struct pairize<a,b> 
{ 
    typedef final_<tpair<a,b>> type; 
}; 

Trong ví dụ sống ở đó cũng là một mã xuất ra tên của tất cả các loại trong một gói tham số để giao diện điều khiển, với demangling, như một thử nghiệm (đã vui hơn để sử dụng hơn mẹo loại không hoàn chỉnh).

+0

Lưu ý rằng bạn đang thiếu mỗi cặp khác (ông cũng muốn 2-3 và 4-5). –

+0

Bạn nói đúng, tôi đọc câu hỏi quá nhanh. Hãy cho tôi thời gian để sửa lỗi này;) – Synxis

+0

Tôi nghĩ rằng đó là một sửa chữa 2 ký tự ;-) –

3

Và bây giờ, chúng ta hãy thử nó với indices và không đệ quy (trừ, tất nhiên, đối với các chỉ số):

#include <tuple> 

template< std::size_t... Ns > 
struct indices 
{ 
    typedef indices< Ns..., sizeof...(Ns) > next; 
}; 

template< std::size_t N > 
struct make_indices 
{ 
    typedef typename make_indices< N - 1 >::type::next type; 
}; 

template<> 
struct make_indices<0> 
{ 
    typedef indices<> type; 
}; 

template< std::size_t, std::size_t > 
struct sometype {}; 

template< typename, typename, typename > 
struct make_pairs; 

template< std::size_t... Ns, std::size_t... Ms, std::size_t... Is > 
struct make_pairs< indices<Ns...>, indices<Ms...>, indices<Is...> > 
{ 
    using type = decltype(std::tuple_cat(
    std::declval< typename std::conditional< Is % 2 == 1, 
              std::tuple< sometype< Ns, Ms > >, 
              std::tuple<> >::type >()... 
)); 
}; 

template< std::size_t... Ns > 
using pairs = typename make_pairs< indices< 0, Ns... >, indices< Ns..., 0 >, 
       typename make_indices< sizeof...(Ns) + 1 >::type >::type; 

int main() 
{ 
    static_assert(std::is_same< pairs<1,2,4,3,5,9>, 
    std::tuple< sometype<1,2>, sometype<4,3>, sometype<5,9> > >::value, "Oops"); 
} 

(OK, tôi đã lừa dối một chút: std::tuple_cat có thể là đệ quy riêng của mình;)


Cập nhật: OK, tôi nên đọc kỹ câu hỏi này. Dưới đây là phiên bản mà tạo ra kết quả mong muốn (indices/make_indices như trên):

template< std::size_t, std::size_t > 
struct sometype {}; 

template< typename, typename, typename > 
struct make_pairs; 

template< std::size_t... Ns, std::size_t... Ms, std::size_t... Is > 
struct make_pairs< indices<Ns...>, indices<Ms...>, indices<Is...> > 
{ 
    using type = decltype(std::tuple_cat(
    std::declval< typename std::conditional< Is != 0 && Is != sizeof...(Is) - 1, 
              std::tuple< sometype< Ns, Ms > >, 
              std::tuple<> >::type >()... 
)); 
}; 

template< std::size_t... Ns > 
using pairs = typename make_pairs< indices< 0, Ns... >, indices< Ns..., 0 >, 
       typename make_indices< sizeof...(Ns) + 1 >::type >::type; 

int main() 
{ 
    static_assert(std::is_same< pairs<1,2,3,4>, 
    std::tuple< sometype<1,2>, sometype<2,3>, sometype<3,4> > >::value, "Oops"); 
} 
+0

+1. Sau hai mươi phút nhìn vào điều này, tôi gần như có thể xoay sở để xem cách thức và lý do nó hoạt động. Nhưng tôi chưa bao giờ có thể tự mình tìm ra được. Kỹ thuật rất thú vị, tôi cần học tốt. –

+0

@AndyProwl Nó có thể trông lạ, nhưng có những trường hợp cần thiết để tránh đệ quy. Nó thường không quan trọng miễn là bạn chỉ cần tính toán các loại, nhưng khi đệ quy liên quan đến việc tạo mã, sự khác biệt là rất lớn! Đó là lý do tại sao tôi thích loại câu hỏi này và tại sao tôi sử dụng chúng để đào tạo, ngay cả khi phải mất nhiều thời gian hơn phiên bản đệ quy đơn giản. –

+0

Thật vậy, điều đó rất thú vị. Tôi nên thực hành điều này nhiều hơn. Tôi nhớ kỹ thuật này vì bạn đã sử dụng nó rồi [trong câu trả lời khác] (http://stackoverflow.com/questions/15411022/how-do-i-replace-a-tuple-element-at-compile-time/15412010#15412010), nhưng nó sẽ không bao giờ đến với tâm trí của tôi nó có thể được sử dụng ở đây. Và sau khi suy nghĩ một chút về nó, có vẻ như nó có thể sử dụng được ở mọi nơi. Nếu bạn có một số ví dụ về các vấn đề mà thành ngữ này mang đến một giải pháp "tự nhiên", hãy mang chúng lên, tôi sẽ biết ơn;) –

Các vấn đề liên quan