17

Tôi có một hàm lấy một tham số có giá trị mặc định. Bây giờ tôi cũng muốn nó có một số tham số biến và chuyển tiếp chúng đến một số hàm khác. Các tham số chức năng với giá trị mặc định phải là cuối cùng, vì vậy ... tôi có thể đặt tham số đó sau gói variadic và trình biên dịch sẽ phát hiện xem tôi đang cung cấp hay không khi gọi hàm?Tham số chức năng mẫu C++ variadic với giá trị mặc định

(Giả sử gói không chứa loại tham số cuối cùng đó. Nếu cần thiết, chúng tôi có thể giả định rằng, vì loại đó thường không được người dùng biết đến, nếu không nó được coi là sử dụng sai của tôi giao diện anyway ....)

template <class... Args> 
void func (Args&&... args, SomeSpecialType num = fromNum(5)) 
{ 
} 

Trả lời

16

Không, gói phải là cuối cùng.

Nhưng bạn có thể giả mạo nó. Bạn có thể phát hiện loại cuối cùng trong gói là gì. Nếu là SomeSpecialType, bạn có thể chạy func của mình. Nếu không phải là SomeSpecialType, bạn có thể tự gọi mình bằng các đối số được chuyển tiếp và fromNum(5) được nối thêm.

Nếu bạn muốn được ưa thích, kiểm tra này có thể được thực hiện tại thời gian biên dịch (tức là, một quá tải khác nhau) bằng cách sử dụng kỹ thuật SFINAE. Nhưng điều đó có lẽ không đáng lo ngại, xem xét việc kiểm tra "run-time" sẽ không đổi trên một tình trạng quá tải, và do đó chắc chắn sẽ được tối ưu hóa, và SFINAE không nên được sử dụng một cách nhẹ nhàng.

Điều này không cung cấp cho bạn chữ ký bạn muốn, nhưng nó cung cấp cho bạn hành vi bạn muốn. Bạn sẽ phải giải thích chữ ký dự định trong các bình luận.

Something như thế này, sau khi bạn loại bỏ lỗi chính tả và những thứ tương tự:

// extract the last type in a pack. The last type in a pack with no elements is 
// not a type: 
template<typename... Ts> 
struct last_type {}; 
template<typename T0> 
struct last_type<T0> { 
    typedef T0 type; 
}; 
template<typename T0, typename T1, typename... Ts> 
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...> {}; 

// using aliases, because typename spam sucks: 
template<typename Ts...> 
using LastType = typename last_type<Ts...>::type; 
template<bool b, typename T=void> 
using EnableIf = typename std::enable_if<b, T>::type; 
template<typename T> 
using Decay = typename std::decay<T>::type; 

// the case where the last argument is SomeSpecialType: 
template< 
    typename... Args, 
    typename=EnableIf< 
    std::is_same< 
     Decay<LastType<Args...>>, 
     SomeSpecialType 
    >::value 
    > 
void func(Args&&... args) { 
    // code 
} 

// the case where there is no SomeSpecialType last:  
template< 
    typename... Args, 
    typename=EnableIf< 
    !std::is_same< 
     typename std::decay<LastType<Args...>>::type, 
     SomeSpecialType 
    >::value 
    > 
void func(Args&&... args) { 
    func(std::forward<Args>(args)..., std::move(static_cast<SomeSpecialType>(fromNum(5)))); 
} 

// the 0-arg case, because both of the above require that there be an actual 
// last type: 
void func() { 
    func(std::move(static_cast<SomeSpecialType>(fromNum(5)))); 
} 

hoặc một cái gì đó nhiều như thế.

+0

Vì vậy, nó giống như một cách giải quyết khác, đó là một chữ ký khác nhưng hành vi tương tự ... tôi hiểu. Trên thực tế tôi đã có kế hoạch để loại bỏ thông số đó trong tương lai, vì vậy có lẽ nó không có giá trị nỗ lực (và chữ ký sẽ gây nhầm lẫn). Bạn có thể chỉ cho tôi một ví dụ đơn giản không? – cfa45ca55111016ee9269f0a52e771

+0

@ fr33domlover Tôi phác họa ra thiết kế. Đã không được biên soạn, hãy để một mình gỡ lỗi, nhưng các nguyên tắc cơ bản nên ở đó. – Yakk

+0

Cảm ơn, tôi sẽ thử nếu tôi không quyết định xóa tham số đơn lẻ. Nó trông phức tạp, và chữ ký không được giữ, vì vậy nó có thể không có giá trị rắc rối ... anyway nhờ – cfa45ca55111016ee9269f0a52e771

3

Một cách tiếp cận khác là chuyển đối số variadic thông qua một bộ tuple.

template <class... Args> 
void func (std::tuple<Args...> t, SomeSpecialType num = fromNum(5)) 
{ 
    // don't forget to move t when you use it for the last time 
} 

Ưu điểm: giao diện đơn giản hơn, quá tải và thêm đối số giá trị mặc định khá dễ dàng.

Nhược điểm: người gọi phải đặt đối số theo cách thủ công trong cuộc gọi std::make_tuple hoặc std::forward_as_tuple. Ngoài ra, bạn có thể sẽ phải sử dụng để std::index_sequence thủ thuật để thực hiện chức năng.

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