2017-06-21 11 views
3

何らかの理由で、下記のコードでclang ++とg ++が100%CPUを使用し、システムがハングするまでメモリをいっぱいにします。範囲の再帰関数(範囲-v3から)は、コンパイルを分岐させます。なぜですか?

に注意してください。これは話のおもちゃの例です。私はaccumulatetransformが標準的な方法であることを認識していますが、このコードは推論の連鎖の中間点です。

#include <iostream> 
#include <range/v3/all.hpp> 

using namespace ranges; 

template <typename F, typename R, typename T> 
T rec_map_sum(F f, R r, T tally) { 
    if (ranges::begin(r) == ranges::end(r)) 
    return tally; 
    else { 
    auto r_head = *ranges::begin(r); 
    auto r_tail = r | view::drop(1); 
    return rec_map_sum(f, r_tail, tally + f(r_head)); 

    // this also crashes: 
    // return rec_map_sum(f, r[{1, end}], tally + f(r_head)); 
    } 
} 

int main() { 
    std::cout << rec_map_sum([](int x) { return x * x; }, view::iota(0, 10), 0) 
      << std::endl; 

    return 0; 
} 

rec_map_sum関数は、整数および単項関数の範囲をとる範囲に機能素子ごとに適用され、マッピングされた要素の和を生成する再帰を実装することを意味します。

2つの質問があります:(1)発散する動作の原因は何ですか、(2)コンパイルがクラッシュしないようにテールビューを作成して渡す必要がありますか?

+4

を出力しますか? – Jarod42

+0

@ Jarod42ああ、私はコンパイラをクラッシュさせてしまったので、条件付きリターンを書くのを忘れてしまった。私はそれを修正します。私のガッフェをすぐに指摘してくれてありがとう。 – Timtro

+1

2番目のブランチは、関数 'rec_map_sum'を無限にインスタンス化します。 – Jarod42

答えて

2

問題はJarod42によって指摘されていますが、解決方法はかなり簡単です。任意の範囲を取ることができるのタイプ消去ビューが必要です。幸いにも、such a thing exists。あなたのコードがに変更します。

template <typename F, typename R, typename T> 
T rec_map_sum(F f, R r, T tally) { 
    auto r2 = any_view<T>{r}; 
    if (ranges::begin(r2) == ranges::end(r2)) 
    return tally; 
    else { 
    auto r_head = *ranges::begin(r2); 
    auto r_tail = r2 | view::drop(1); 
    return rec_map_sum(f, r_tail, tally + f(r_head)); 
    } 
} 
これは再帰の終了を処理するにはどうすればよい285

関連する問題