可変個のテンプレート:グループで引数を展開


16

2つの引数を取る関数があります。

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
同じ引数を2回転送するのは危険です
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

n-aryファンクタのC ++ 17汎化:

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.