2012-04-16 30 views
5

Nếu tôi có một đối tượng a hoặc là một mảng tích hợp hoặc loại lớp với operator [] phù hợp và loại trả lại của nó có thể được lập chỉ mục, tôi nên viết một hàm chung có thể lập chỉ mục tất cả chúng bằng một biến thể variadic như thế nào gọi thay vì khối khung được phân tách? Nói cách khác, tôi có thể làm cho biểu thức như:Làm cách nào để tạo một hàm lập chỉ mục xích dựa trên variadic?

a[i0][i1]...[iK] 

và tôi muốn để có thể viết nó như là một chức năng duy nhất:

slice(a, i0, i1, ..., iK) 

từ các quy tắc của C++ yêu cầu operator [] để làm việc trên lập luận đơn , làm cho nó ít tương thích với công cụ variadic. (Câu hỏi này được dựa trên một sợi Usenet nơi tôi thử hỏi một cái gì đó tương tự; kết thúc giải quyết chỉ có thể-lồng built-in mảng bản thân mình.)

Một đâm đầu tiên:

template < typename T, typename U > 
constexpr 
auto slice(T &&t, U &&u) noexcept(???) -> ??? 
{ return ce_forward<T>(t)[ ce_forward<U>(u) ]; } 

template < typename T, typename U, typename V, typename ...W > 
constexpr 
auto slice(T &&t, U &&u, V &&v, W... &&w) noexcept(???) -> ??? 
{ 
    return slice(ce_forward<T>(t)[ce_forward<U>(u)], ce_forward<V>(v), 
    ce_forward<W>(w)...); 
} 

Các ce_forward chức năng mẫu là a constexpr -marked std::forward. (Một số điều trong tiêu chuẩn có thể được đánh dấu constexpr không.) Tôi đang cố gắng tìm ra những thứ phù hợp để đưa vào kiểu trả về và các điểm đặc tả ngoại lệ. Một số trường hợp và hãy cẩn thận tôi biết là:

  • Việc xây dựng trong operator [] đòi hỏi một toán hạng là một dữ liệu con trỏ (hoặc tham chiếu mảng phân rã) và người kia là một liệt kê hoặc số nguyên kiểu. Các toán hạng có thể theo thứ tự. Tôi không biết liệu một toán hạng có thể được thay thế bằng một kiểu lớp với một chuyển đổi rõ ràng (không rõ ràng?) Thành một kiểu thích hợp hay không.
  • Loại lớp có thể xác định operator [] là (các) hàm thành viên không tĩnh. Bất kỳ chức năng nào như vậy chỉ có thể có một tham số (bên cạnh this).
  • Nhà điều hành tích hợp không thể ném. (Trừ khi toán hạng có thể dựa trên một chuyển đổi UDT, chuyển đổi nói là điểm ném duy nhất có thể.) Hành vi không xác định do lỗi biên là vượt quá tầm nhìn của chúng tôi ở đây.
  • Cuộc gọi lập chỉ mục cuối cùng có trả về tham chiếu giá trị l hay không, tham chiếu giá trị r hoặc giá trị phải được phản ánh trong loại trả về là slice.
  • Nếu cuộc gọi (cuối cùng?) Là giá trị bằng, thì std::is_nothrow_move_constructible<ReturnType>::value phải là OR cho đặc tả ngoại lệ. (Trả về tham chiếu là noexcept.)
  • Nếu toán tử tích hợp liên quan đến mảng, tham chiếu trả lại cho bước đó phải là tham chiếu giá trị r nếu mảng đó cũng là một tham chiếu. (Đây là loại lỗi bởi vì con trỏ, không giống như mảng, mất trạng thái giá trị l- so với giá trị r của chúng, do đó trở lại truyền thống sẽ luôn là tham chiếu giá trị l.)
  • Đối với toán tử lập chỉ mục lớp, đảm bảo các phần loại trừ-spec và trả về tham chiếu đến cùng một tình trạng quá tải (nếu có nhiều hơn) mà nội dung chức năng sử dụng. Hãy cảnh giác với tình trạng quá tải chỉ khác nhau trong tiêu chuẩn của this (const/volatile/cả hai/không và/hoặc &/&&/không).
+0

Nó phải là 'constexpr'? – kennytm

+0

Đối với những gì tôi đang sử dụng nó cho, nó phải là 'constexpr' bất cứ khi nào có thể. – CTMacUser

+1

Bạn đã xem xét việc đặt cùng một biểu thức như trong câu lệnh 'return' cho cả kiểu trả về và đặc tả' noexcept'? Ví dụ. 'noexcept (noexcept (std :: declval () [std :: declval ()])) -> decltype (std :: declval () [std :: declval ()])'. –

Trả lời

3

Tôi đã đưa ra giải pháp dựa trên nhận xét của @ luc-danton và câu trả lời của @ user315052.

#include <utility> 

template < typename Base, typename ...Indices > 
class indexing_result; 

template < typename T > 
class indexing_result<T> 
{ 
public: 
    using type = T; 

    static constexpr 
    bool can_throw = false; 
}; 

template < typename T, typename U, typename ...V > 
class indexing_result<T, U, V...> 
{ 
    using direct_type = decltype(std::declval<T>()[std::declval<U>()]); 
    using next_type = indexing_result<direct_type, V...>; 

    static constexpr 
    bool direct_can_throw 
    = not noexcept(std::declval<T>()[std::declval<U>()]); 

public: 
    using type = typename next_type::type; 

    static constexpr 
    bool can_throw = direct_can_throw || next_type::can_throw; 
}; 

template < typename T > 
inline constexpr 
auto slice(T &&t) noexcept -> T && 
{ return static_cast<T &&>(t); } 

template < typename T, typename U, typename ...V > 
inline constexpr 
auto slice(T &&t, U &&u, V &&...v) 
    noexcept(!indexing_result<T, U, V...>::can_throw) 
    -> typename indexing_result<T, U, V...>::type 
{ 
    return slice(static_cast<T &&>(t)[static_cast<U &&>(u)], 
    static_cast<V &&>(v)...); 
} 

Tôi đặt full example program làm Gist. Nó hoạt động trên một trình biên dịch trang web chạy GCC> = 4.7, CLang> = 3.2 và Intel C++> = 13.0.

+0

'bool direct_can_throw = not noceptcept (...)' là 'không' được cho là có? – 0x499602D2

+0

@ 0x499602D2, vâng. Các công cụ 'noexcept' muốn biết khi nào chúng ta làm * không * ném, nhưng lớp của tôi tính toán khi nào để ném. – CTMacUser

+0

Ồ vâng tôi quên từ khóa 'không' giống như'! ':) – 0x499602D2

1

Để nhận giá trị trả về phù hợp cho hàm slice, bạn có thể tạo mẫu trình trợ giúp tính toán loại kết quả chính xác.Ví dụ:

template <typename T, typename U, typename... V> 
struct SliceResult { 
    typedef typename SliceResult<T, V...>::Type Type; 
}; 

template <typename T, typename U> 
struct SliceResult<T, U> { 
    typedef typename std::underlying_type<T[U(0)]>::type Type; 
}; 

Chức năng slice sau đó sẽ trả lại SliceResult<...>::Type.

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