在for循环中避免if语句?
我有一个名为Writer
的类,它具有如下所示的函数writeVector
:
void Drawer::writeVector(vector<T> vec, bool index=true) { for (unsigned int i = 0; i < vec.size(); i++) { if (index) { cout << i << "\t"; } cout << vec[i] << "\n"; } }
我试图不要有重复的代码,同时仍然担心性能。 在函数中,我正在对我的for
循环的每一轮执行if (index)
检查,即使结果总是相同的。 这是对“担心performance”的反对。
我可以很容易地避免这种情况,把支票放在我以外的地方。 但是,我会得到大量重复的代码:
void Drawer::writeVector(...) { if (index) { for (...) { cout << i << "\t" << vec[i] << "\n"; } } else { for (...) { cout << vec[i] << "\n"; } } }
所以这些对我来说都是“坏”的解决scheme。 我一直在想的是两个私人的function,其中一个是索引,然后是另一个。 另一个只是价值。 但是,我不知道如何使用它与我的程序,我仍然需要if
检查,看看哪一个调用…
根据这个问题,多态性似乎是一个正确的解决scheme。 但是我看不出在这里怎么用。 什么是解决这类问题的首选方法?
这不是一个真正的程序, 我只是想学习如何解决这个问题。
作为函数传入循环的主体。 它在编译时被内联,没有性能损失。
传递变化的想法在C ++标准库中是无处不在的。 这就是所谓的战略模式。
如果你被允许使用C ++ 11,你可以这样做:
#include <iostream> #include <set> #include <vector> template <typename Container, typename Functor, typename Index = std::size_t> void for_each_indexed(const Container& c, Functor f, Index index = 0) { for (const auto& e : c) f(index++, e); } int main() { using namespace std; set<char> s{'b', 'a', 'c'}; // indices starting at 1 instead of 0 for_each_indexed(s, [](size_t i, char e) { cout<<i<<'\t'<<e<<'\n'; }, 1u); cout << "-----" << endl; vector<int> v{77, 88, 99}; // without index for_each_indexed(v, [](size_t , int e) { cout<<e<<'\n'; }); }
这个代码并不完美,但你明白了。
在旧的C ++ 98中,它看起来像这样:
#include <iostream> #include <vector> using namespace std; struct with_index { void operator()(ostream& out, vector<int>::size_type i, int e) { out << i << '\t' << e << '\n'; } }; struct without_index { void operator()(ostream& out, vector<int>::size_type i, int e) { out << e << '\n'; } }; template <typename Func> void writeVector(const vector<int>& v, Func f) { for (vector<int>::size_type i=0; i<v.size(); ++i) { f(cout, i, v[i]); } } int main() { vector<int> v; v.push_back(77); v.push_back(88); v.push_back(99); writeVector(v, with_index()); cout << "-----" << endl; writeVector(v, without_index()); return 0; }
代码再一次没有完美,但它给了你这个想法。
在函数中,我正在对for循环的每一轮执行if(index)检查,即使结果总是相同的。 这是对“担心performance”的反对。
如果确实如此,则分支预测器在预测(常量)结果时将没有问题。 因此,这只会对前几次迭代中的预测错误造成轻微的开销。 在性能方面没有什么可担心的
在这种情况下,为了清晰起见,我主张保持循环内部的testing。
为了扩大阿里的答案,这是完全正确的,但仍然重复一些代码(循环体的一部分,这是不幸的,当使用策略模式是不可避免的)…
在这种特殊情况下,代码重复并不多,但是还有一种方法可以减less代码的重复次数, 如果函数体大于几个指令,这个函数就派上用场了 。
关键是要使用编译器的能力来执行常量折叠/死代码消除 。 我们可以通过将index
的运行时间值手动映射到编译时间值(在只有有限数量的情况下很容易实现 – 在本例中为两个),并使用已知的非types模板参数编译时间:
template<bool index = true> // ^^^^^^ note: the default value is now part of the template version // see below to understand why void writeVector(const vector<int>& vec) { for (size_t i = 0; i < vec.size(); ++i) { if (index) { // compile-time constant: this test will always be eliminated cout << i << "\t"; // this will only be kept if "index" is true } cout << vec[i] << "\n"; } } void writeVector(const vector<int>& vec, bool index) // ^^^^^ note: no more default value, otherwise // it would clash with the template overload { if (index) // runtime decision writeVector<true>(vec); // ^^^^ map it to a compile-time constant else writeVector<false>(vec); }
这样我们最终编译的代码就等于你的第二个代码示例(outer if
/ inner for
),但是不需要自己复制代码。 现在,我们可以使模板版本的writeVector
像我们想要的那样复杂,总会有一段代码需要维护。
请注意,模板版本(以非types模板参数forms的编译时常量)和非模板版本(以运行时variables作为函数参数)是如何重载的。 这允许您根据您的需要select最相关的版本,在这两种情况下都具有相似的,易于记忆的语法:
writeVector<true>(vec); // you already know at compile-time which version you want // no need to go through the non-template runtime dispatching writeVector(vec, index); // you don't know at compile-time what "index" will be // so you have to use the non-template runtime dispatching writeVector(vec); // you can even use your previous syntax using a default argument // it will call the template overload directly