使用C ++ 11,STL现在具有std::iota
功能(参见reference)。与std::fill_n
,std::generate_n
相比,没有std::iota_n
。什么是一个很好的实现呢?使用简单的lambda表达式(替代2)直接循环(替代1)或委派给std::generate_n
?
备选方案1)
template<class OutputIterator, class Size, class T>
OutputIterator iota_n(OutputIterator first, Size n, T value)
{
while (n--)
*first++ = value++;
return first;
}
备选方案2)
template<class OutputIterator, class Size, class T>
OutputIterator iota_n(OutputIterator first, Size n, T value)
{
return std::generate_n(first, n, [&](){ return value++; });
}
两种替代方案都会生成与优化编译器相同的代码吗?
UPDATE :结合了@Marc Mutz的优点,也在目标点返回迭代器。这也是C ++ 11与C ++ 98相比std::generate_n
更新的方式。
答案 0 :(得分:10)
作为一个随机的例子,我使用g++ -S -O2 -masm=intel
(GCC 4.7.1,x86_32)编译了以下代码:
void fill_it_up(int n, int * p, int val)
{
asm volatile("DEBUG1");
iota_n(p, n, val);
asm volatile("DEBUG2");
iota_m(p, n, val);
asm volatile("DEBUG3");
for (int i = 0; i != n; ++i) { *p++ = val++; }
asm volatile("DEBUG4");
}
此处iota_n
是第一个版本,iota_m
是第二个版本。这三个案例中的汇编是:
test edi, edi
jle .L4
mov edx, eax
neg edx
lea ebx, [esi+edx*4]
mov edx, eax
lea ebp, [edi+eax]
.p2align 4,,7
.p2align 3
.L9:
lea ecx, [edx+1]
cmp ecx, ebp
mov DWORD PTR [ebx-4+ecx*4], edx
mov edx, ecx
jne .L9
使用-O3
,这三个版本也非常相似,但批次更长(使用条件移动和punpcklqdq
等)。
答案 1 :(得分:3)
你如此专注于代码生成,忘了让界面正确。
您正确需要OutputIterator
,但如果您想再次拨打电话会怎样?
list<double> list(2 * N);
iota_n(list.begin(), N, 0);
// umm...
iota_n(list.begin() + N, N, 0); // doesn't compile!
iota_n(list.rbegin(), N, 0); // works, but create 0..N,N-1..0, not 0..N,0..N
auto it = list.begin();
std::advance(it, N);
iota_n(it, N, 0); // works, but ... yuck and ... slow (O(N))
在iota_n
内,您仍然知道自己在哪里,但是您已经抛弃了这些信息,因此呼叫者无法在固定时间内获取信息。
一般原则:不要丢弃有用的信息。
template <typename OutputIterator, typename SizeType, typename ValueType>
auto iota_n(OutputIterator dest, SizeType N, ValueType value) {
while (N) {
*dest = value;
++dest;
++value;
--N;
}
// now, what do we know that the caller might not know?
// N? No, it's zero.
// value? Maybe, but it's just his value + his N
// dest? Definitely. Caller cannot easily compute his dest + his N (O(N))
// So, return it:
return dest;
}
通过这个定义,上面的例子变得简单:
list<double> list(2 * N);
auto it = iota_n(list.begin(), N, 0);
auto end = iota_n(it, N, 0);
assert(end == list.end());
答案 2 :(得分:0)
假设的iota_n
std::iota_n(first, count, value)
可以用一根衬纸代替。
std::generate_n(first, count, [v=value]()mutable{return v++;})
我更喜欢它具有标准中没有的缠绵功能。话虽如此,我认为std::iota_n
应该在标准中。
答案 3 :(得分:0)
可能将 back_insertor 与 std::generate_n 一起使用,这样可以避免集合的预分配。
vector<int> v3;
generate_n(back_inserter(v3),10,[i=1]() mutable{
return i++;
});
我们可以把它换成 iota_n 直到我们得到 iota_n :)