アプリケーション開発ポータルサイト
ServerNote.NET
Amazon.co.jpでPC関連商品タイムセール開催中!
カテゴリー【C/C++
【C++】std::for_eachでbreakする方法
POSTED BY
2023-07-28

何もトリッキーなことしなくても、std::for_eachをstd::all_ofに置き換えればいいだけです。

std::all_ofはすべての配列内要素が特定の条件を満たすか判定するループで、trueを返し続ける限り続き、falseを返した時点でループ終了=これが完全にbreakの代わりとなる=です。

以下サンプル

C/C++break_for_each.cppGitHub Source
#include <algorithm>
#include <iostream>
#include <vector>
#include <memory>
#include <string>

class Test {
public:
  std::string s_val;
  int n_val;
  Test(std::string s, int n) {
    s_val = s;
    n_val = n;
  }
};

int main(int argc, char **argv) {

  std::vector<std::shared_ptr<Test>> tests = std::vector<std::shared_ptr<Test>>();

  tests.push_back(std::make_shared<Test>(std::string("あ"), 1));
  tests.push_back(std::make_shared<Test>(std::string("い"), 2));
  tests.push_back(std::make_shared<Test>(std::string("う"), 3));
  tests.push_back(std::make_shared<Test>(std::string("え"), 4));
  tests.push_back(std::make_shared<Test>(std::string("お"), 5));

  std::for_each(tests.begin(), tests.end(), [&](auto &c) {
    std::cout << c->s_val << std::endl;
  });

  std::all_of(tests.begin(), tests.end(), [&](auto &c) {
    if(c->n_val >= 4) {
      return false; // break
    }
    std::cout << c->s_val << std::endl;
    return true;
  });

  return 0;
}

コンパイル、実行結果

 g++ break_for_each.cpp

./a.out

あ
い
う
え
お
あ
い
う

まず最初にTestクラス配列をfor_eachでスタンダードに回します。breakする手段はありません。
次にall_ofに置き換えて、n_valが4以上なら抜ける=falseを返す=breakの代わり=を実現しています。
テスト出力も「う」までしか出ていないことから、途中で抜けたことがわかります。

さて、こんな目的外の用途をしてパフォーマンスは大丈夫か?と気になるところですので、以下、Testクラス配列を10万個生成しfor_eachとall_of両方のループをミリ秒計測で比較してみましたサンプルです。

C/C++break_for_each_bench.cppGitHub Source
#include <algorithm>
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <time.h>
#include <sys/time.h>

class Test {
public:
  std::string s_val;
  int n_val;
  Test(std::string s, int n) {
    s_val = s;
    n_val = n;
  }

  static double getmicrotime(void) {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (((double)tv.tv_sec)*((double)1000000)+((double)tv.tv_usec));
  }
};

int main(int argc, char **argv) {

  std::vector<std::shared_ptr<Test>> tests = std::vector<std::shared_ptr<Test>>();

  int i; char buf[256]; double mt_fr, mt_to;

  for(i = 0; i < 100000; i++) {
    sprintf(buf, "要素%d", i);
    tests.push_back(std::make_shared<Test>(std::string(buf), i));
  }

  std::cout << "tests counts=" << tests.size() << std::endl;


  mt_fr = Test::getmicrotime();

  std::for_each(tests.begin(), tests.end(), [&](auto &c) {
    if(c->n_val % 10000 == 0) {
      std::cout << c->s_val << std::endl;
    }
  });

  mt_to = Test::getmicrotime();

  std::cout << "std::for_each loop time = " << (mt_to - mt_fr) / 1000 << "ms" << std::endl;

  mt_fr = Test::getmicrotime();

  std::all_of(tests.begin(), tests.end(), [&](auto &c) {
    if(c->n_val % 10000 == 0) {
      std::cout << c->s_val << std::endl;
    }
    return true;
  });

  mt_to = Test::getmicrotime();

  std::cout << "std::all_of loop time = " << (mt_to - mt_fr) / 1000 << "ms" << std::endl;

  mt_fr = Test::getmicrotime();

  std::all_of(tests.begin(), tests.end(), [&](auto &c) {
    if(c->n_val >= 50000) {
      return false;
    }
    if(c->n_val % 10000 == 0) {
      std::cout << c->s_val << std::endl;
    }
    return true;
  });

  mt_to = Test::getmicrotime();

  std::cout << "std::all_of break loop time = " << (mt_to - mt_fr) / 1000 << "ms" << std::endl;

  return 0;
}

コンパイル、実行結果

g++ break_for_each_bench.cpp

./a.out

tests counts=100000

要素0
要素10000
要素20000
要素30000
要素40000
要素50000
要素60000
要素70000
要素80000
要素90000
std::for_each loop time = 1.74ms

要素0
要素10000
要素20000
要素30000
要素40000
要素50000
要素60000
要素70000
要素80000
要素90000
std::all_of loop time = 1.556ms

要素0
要素10000
要素20000
要素30000
要素40000
std::all_of break loop time = 0.993ms

フルループの比較では、両者の性能に差はありません。さて、50000要素目で抜ける=breakする=all_ofですが、50%途中で抜けているので50%の削減=0.7~8msほどになってほしいところですが、これは、ループを抜ける比較判定処理

if(c->n_val >= 50000) {
   return false;
}

が入っているから、多少余計にミリ秒がかかっているものと思われます。よって、パフォーマンスの低下は無くbreakが実現できているといえます。

※本記事は当サイト管理人の個人的な備忘録です。本記事の参照又は付随ソースコード利用後にいかなる損害が発生しても当サイト及び管理人は一切責任を負いません。
※本記事内容の無断転載を禁じます。
【WEBMASTER/管理人】
自営業プログラマーです。お仕事ください!
ご連絡は以下アドレスまでお願いします★

☆ServerNote.NETショッピング↓
ShoppingNote / Amazon.co.jp
☆お仲間ブログ↓
一人社長の不動産業務日誌
【キーワード検索】