Mẫu biến thể: mở ra các đối số trong nhóm


16

Tôi có một hàm có hai đối số:

template <typename T1, typename T2>
void foo(T1 arg1, T2 arg2)
{ std::cout << arg1 << " + " << arg2 << '\n'; }

Và một biến thể nên chuyển tiếp các đối số của nó theo cặp:

template <typename... Args>
void bar(Args&&... args) {
    static_assert(sizeof...(Args) % 2 == 0);

    ( foo( std::forward<Args>(args), std::forward<Args>(args) ), ... );
    // ^ Sends each argument twice, not in pairs
}

Tôi muốn bar(1,2,3,4)gọi foo(1,2)foo(3,4)

Có cách nào làm được việc này không ?


4
Thật nguy hiểm khi chuyển tiếp cùng một cuộc tranh luận hai lần
AndyG

Câu trả lời:


13

Bạn có thể hoàn thành nó với quá tải.

template <typename T1, typename T2>
void bar(T1&& arg1, T2&& arg2) {
    foo( std::forward<T1>(arg1), std::forward<T2>(arg2) ); // (until) sends (the last) two arguments to foo
}

template <typename T1, typename T2, typename... Args>
void bar(T1&& arg1, T2&& arg2, Args&&... args) {
    foo( std::forward<T1>(arg1), std::forward<T2>(arg2) ); // sends the 1st two arguments to foo
    bar( std::forward<Args>(args)... );                    // call bar with remaining elements recursively
}

TRỰC TIẾP


Lưu ý rằng với đoạn mã tối thiểu ở trên khi gọi barbằng 0 hoặc đối số lẻ, bạn sẽ không gặp lỗi chức năng phù hợp . Nếu bạn muốn thông báo biên dịch rõ ràng hơn với static_assertbạn, bạn có thể bắt đầu từ đoạn trích này .


5

Đệ quy đơn giản sử dụng if constexpr:

// print as many pairs as we can
template<class T, class U, class... Args>
void foo(T t, U u, Args&&... args)
{
    std::cout << t << " + " << u << "\n";
    if constexpr(sizeof...(Args) > 0 && sizeof...(Args) % 2 == 0)
        foo(std::forward<Args>(args)...);
}

template<class... Args>
void bar(Args&&... args)
{
    static_assert(sizeof...(Args) % 2 == 0);
    foo(std::forward<Args>(args)...);
}

Gọi nó như vậy:

bar(1, 2, 3, 4);

Bản giới thiệu

Tôi muốn nói rằng câu trả lời của songyanyao là khá chuẩn trước C ++ 17. Sau đó, if constexprđã cho phép chúng tôi di chuyển logic vào cơ thể của các chức năng thay vì sử dụng các thủ thuật quá tải.


1
Phiên bản của songyanyao khá dễ dàng để mở rộng, do đó, nó cũng lấy chức năng mà nó đang áp dụng làm đối số. Theo tôi điều này khá hay, vì nó cho phép chúng ta áp dụng mô hình này nhiều lần mà không phải viết logic mọi lúc. Có một phiên bản câu trả lời của bạn cho phép giống nhau không?
n314159

1
@ n314159: Một cái gì đó như thế này ?
AndyG

1
Chính xác! Cảm ơn bạn. Cá nhân tôi thích điều này, vì (ngoài những gì tôi đã nói) nó tách biệt logic áp dụng từ chức năng mà nó được áp dụng.
n314159

2

C ++ 17 khái quát hóa cho nfunctor -ary:

namespace impl
{
    template<std::size_t k, class Fn, class Tuple, std::size_t... js>
    void unfold_nk(Fn fn, Tuple&& tuple, std::index_sequence<js...>) {
        fn(std::get<k + js>(std::forward<Tuple>(tuple))...);
    }

    template<std::size_t n, class Fn, class Tuple, std::size_t... is>
    void unfold_n(Fn fn, Tuple&& tuple, std::index_sequence<is...>) {
        (unfold_nk<n * is>(fn, std::forward<Tuple>(tuple), 
            std::make_index_sequence<n>{}), ...);
    }
}

template<std::size_t n, class Fn, typename... Args>
void unfold(Fn fn, Args&&... args) {
    static_assert(sizeof...(Args) % n == 0);
    impl::unfold_n<n>(fn, std::forward_as_tuple(std::forward<Args>(args)...), 
        std::make_index_sequence<sizeof...(Args) / n>{});
}

int main() {
    auto fn = [](auto... args) { 
        (std::cout << ... << args) << ' ';
    };

    unfold<2>(fn, 1, 2, 3, 4, 5, 6);   // Output: 12 34 56
    unfold<3>(fn, 1, 2, 3, 4, 5, 6);   // Output: 123 456
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.