にほんごのれんしゅう

日本語として伝えるための訓練を兼ねたテクログ

STL algoritmのfor_eachする手法のいろいろ.私の時代順

STL algoritmのfor_eachする手法のいろいろ.私の時代順

motivation

lambda式を利用したシンタックスに移行する際に,いくつかリーダビリティが下がるのではないのかという仮説があった.
たとえば,STL algorithmのfor_eachを用いると,forの代わりになるいくつかの書き方を紹介する.
lambdaが導入されるまでに,いくつか問題点があることを示す.
forを使えばいいじゃないかというそもそも論は今回は考慮しない.あくまでlambdaに到達するまでの遷移である.

実装内容

関数オブジェクトないし,lambdaでloopを処理する.

実装1 C++98レベル

昔からある書き方である.
関数オブジェクトを用い,operator()を定義して処理する.

  • デメリット
    使用する関数の外でロジックを定義しなくてはならない.そのため,使用側と,ロジックのコード上での距離が離れ,よろしくない.
    operator()には引数が二つ以上とることができないので,あんまりきれいな書き方ができない.
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
//関数オブジェクトを用いて実装する方法
std::string suffix_ = "Hello, "
struct funcObject{
  void operator()(std::string& it) {
    std::cout << suffix_ + it << std::endl;
  }
};
void eval1(){
  std::vector<std::string> v = {"aaa", "bbb", "ccc"};
  std::for_each(v.begin(), v.end(), funcObject());
}

実装2 C++03 とboostを用いたレベル

関数内関数を用いて実装する.boost::bind(例ではstd::bind)を用いている

  • デメリット
    bindのシンタックスを知る必要がある.
    operator()の代わりに別の関数名を定義しているが,staticにしなくてはならない.非同期な処理を行う際にstaticを持ちるのは心配である.
//関数内関数を用いて実装する方法
void eval2(){
  struct innerFunc{
    static void func(std::string& suf, std::string& it) {
      std::cout << suf + it << std::endl;
    }
  };
  std::vector<std::string> v = {"aaa", "bbb", "ccc"};
  std::string suffix = "Hello, ";
  std::for_each(v.begin(), v.end(), std::bind(&innerFunc::func, suffix, std::placeholders::_1) );
}

実装3 C++14でlambdaを用いる例

generic lambdaを利用している.
デメリットは特にないと思う.

//generic-lambda式を用いて実装する方法
void eval3(){
  std::vector<std::string> v = {"aaa", "bbb", "ccc"};
  std::string suffix = "Hello, ";
  std::for_each(v.begin(), v.end(), [suffix](auto s){
      std::cout << suffix + s << std::endl;
  });
}

C++14にgeneric-lambdaが提案,実装されるまでの道のりは長かった...

  • 以下のreadabilityの問題がある
    関数オブジェクトはtemplateが使えるため,C++11のただのlambdaより使いやすかったという背景がある.しかし,長い関数の中では外部に定義しなくてはいけないという問題があって,悩みどころではあった.
    コード上でロジックを距離的に近づけたbindを用いる方法もあるが,staticをつける必要があり,async, future, promisなどと組み合わせたとき,いいのか悪いのか疑問がかなりあった.そもそも,非同期で処理を実行するときは,処理の流れ自体を別のインスタンスにして相互に干渉し合わせないほうがいいという,私独自の先入観があるかもしれない.

  • lambdaが導入されることで解決された問題   lambdaが導入されると,readability, staticの問題は解決され,だいぶやりやすい.lambda使おう.