เทมเพลต Variadic: เปิดเผยอาร์กิวเมนต์ในกลุ่ม


16

ฉันมีฟังก์ชั่นที่รับสองอาร์กิวเมนต์:

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

และอีกหนึ่งตัวแปรที่ควรส่งต่อข้อโต้แย้งเป็นคู่:

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
}

ฉันต้องการbar(1,2,3,4)โทรfoo(1,2)และfoo(3,4)

มีวิธีทำเช่นนั้นหรือไม่?


4
มันอันตรายที่จะส่งต่อสิ่งที่เหมือนกันสองครั้ง
AndyG

คำตอบ:


13

คุณสามารถทำได้ด้วยการโอเวอร์โหลด

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
}

มีชีวิต


โปรดทราบว่าเมื่อมีข้อมูลโค้ดขั้นต่ำด้านบนเมื่อโทรbarด้วยอาร์กิวเมนต์ 0 หรือคี่คุณจะไม่ได้รับข้อผิดพลาดของฟังก์ชันที่ตรงกัน หากคุณต้องการข้อความรวบรวมที่ชัดเจนยิ่งขึ้นstatic_assertคุณสามารถเริ่มจากตัวอย่างนี้


5

ใช้การเรียกซ้ำแบบง่าย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)...);
}

เรียกว่าเป็นเช่นนั้น:

bar(1, 2, 3, 4);

การสาธิต

ฉันจะบอกว่าคำตอบของ songyanyaoเป็นที่ยอมรับสวย pre-C ++ 17 หลังจากนั้นif constexprอนุญาตให้เราย้ายตรรกะเข้าสู่เนื้อหาของฟังก์ชันแทนที่จะใช้กลอุบายมากไป


1
เวอร์ชั่นของ Songyanyao นั้นค่อนข้างง่ายต่อการขยายเช่นว่ามันยังใช้ฟังก์ชั่นที่ใช้เป็นอาร์กิวเมนต์ ในความคิดของฉันมันค่อนข้างดีเพราะมันช่วยให้เราสามารถใช้รูปแบบนี้หลายครั้งโดยไม่ต้องเขียนตรรกะทุกครั้ง มีรุ่นของคำตอบที่อนุญาตเหมือนกันหรือไม่?
n314159

1
@ n314159: อะไรแบบนี้ ?
AndyG

1
แน่นอน! ขอบคุณ. โดยส่วนตัวแล้วฉันชอบสิ่งนี้เนื่องจาก (นอกเหนือจากสิ่งที่ฉันพูดไปแล้ว) มันแยกตรรกะการใช้งานออกจากฟังก์ชั่นที่ใช้กับมัน
n314159

2

ลักษณะทั่วไปของ C ++ 17 สำหรับn--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
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.