次のような状況を想定します。

int a;
void plus_a_v1(int& elem)
  elem += a;
int main() {
  a = foo(); // aはruntimeで決まると仮定。constexprにならない。
  int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  for(int* ptr=array; ptr != array+10; ++ptr)
    plus_a_v1(*ptr);
  return 0;
}

上記のような場合はaがどうしてもruntimeで決まるしかない状況でかつほぼすべての関数で使われるような変数であればある種の「環境」としてaを使っています。しかし、これではaを間違えて変更してしまったりする場合は問題になるので、次のようにプログラムを組み直しました。

void plus_a_v2(int& elem, const int a)
  elem += a;
int main() {
  const int a = foo();
  int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  for(int* ptr=array; ptr != array+10; ++ptr)
    plus_a_v2(*ptr, a);
  return 0;
}

plus_a_v1とplus_a_v2の間の性能差はありますか?

そして、後に環境が増えてきて、次のように定数の環境だけのための構造体Envを作った場合、Envの参照を変数として渡すplus_a_v3の実装はplus_a_v2およびplus_a_v1との理論的性能差はありますか?一応plus_a_v3のほうが構造体を通じて参照しているので、性能上少し劣りそうですが…

struct Env {
  int a;
  int b;
};
void plus_a_v3(int& elem, const Env& e)
  elem += e.a;
int main() {
  const Env e = foo_Env(); // 環境定数を設定
  int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  for(int* ptr=array; ptr != array+10; ++ptr)
    plus_a_v3(*ptr, e);
  return 0;
}

また、次のような場合も追加で考えられそうです。一応、これはaが一度決まった後は定数だということに基づきます。

struct plus_a_functor {
  const int a; 
  plus_a_functor (const int a) : a(a) { }
  void operator() (int& elem) const {
    elem += a;
  }
};
int main() {
  const int a = foo();
  plus_a_functor plus_a_v4(a);
  int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  for(int* ptr=array; ptr != array+10; ++ptr)
    plus_a_v4(*ptr);
  return 0;
}

plus_a_v1, 2, 3, 4 それぞれの性能は理論的にどのように違いがありますか?