c++ stl中的坑
主要摘自https://www.zhihu.com/question/355171938
no.1
std::string GetString()
{
std::string str = "hello world";
return std::move(str); // bad
return str; // ok
}
针对这个问题,我在compiler explorer中做了测试(https://gcc.godbolt.org/),在没有优化的版本,gcc的汇编结果中可以看到,bad的那种情况,多做了一次移动构造,而ok的那种情况则没有。
这次看汇编代码也让我更加熟悉了一丢丢64位x64的c++汇编,不再是32位中ecx作为this指针,64位中函数的传参是当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9
参数为7个以上时, 前 6 个与前面一样, 但后面的依次从 “右向左” 放入栈中,即和32位汇编一样。
参考http://abcdxyzk.github.io/blog/2012/11/23/assembly-args/
我还在https://quick-bench.com中对两种不同的返回方式的速度进行了测试
测试代码如下
#include <string>
#include <iostream>
// Type your code here, or load an example.
std::string GetString1()
{
std::string str = "hello world";
return std::move(str); // bad
}
std::string GetString2()
{
std::string str = "hello world";
return str;
}
void func1(benchmark::State& state)
{
for (auto _ : state) {
std::string s = GetString1();
benchmark::DoNotOptimize(s);
}
}
void func2(benchmark::State& state)
{
for (auto _ : state) {
std::string s = GetString2();
benchmark::DoNotOptimize(s);
}
}
BENCHMARK(func1);
BENCHMARK(func2);
可以看到,即使用了O3或者OFast,gcc编译的程序中,还是func1还是会显著慢于func2,在clang中则似乎被优化成一样的了。
no.2
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main()
{
vector<float> v{0.2,0.3,0.4};
double sum = accumulate(begin(v),end(v),0);
cout<<"sum = "<<sum<<endl;//output: sum = 0
return 0;
}
这个问题是由于accumulate的类型参数导致的,见https://en.cppreference.com/w/cpp/algorithm/accumulate
Common mistakes
If left to type inference, op
operates on values of the same type as init
which can result in unwanted casting of the iterator elements. For example, std::accumulate(v.begin(), v.end(), 0) likely does not give the result one wishes for when v
is std::vector<double>.
我自己再补充个坑:
std::lower_bound
default (1) | template <class ForwardIterator, class T> ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val); |
---|---|
custom (2) | template <class ForwardIterator, class T, class Compare> ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp); |
对comp参数的要求为:
comp Binary function that accepts two arguments (the first of the type pointed by ForwardIterator, and the second, always val), and returns a value convertible to bool
. The value returned indicates whether the first argument is considered to go before the second.
The function shall not modify any of its arguments.
This can either be a function pointer or a function object.
std::upper_bound
default (1) | template <class ForwardIterator, class T> ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val); |
---|---|
custom (2) | template <class ForwardIterator, class T, class Compare> ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp); |
对comp函数的要求为:
Binary function that accepts two arguments (the first is always val, and the second of the type pointed by ForwardIterator), and returns a value convertible to bool
. The value returned indicates whether the first argument is considered to go before the second.
The function shall not modify any of its arguments.
This can either be a function pointer or a function object.
虽然lower_bound和upper_bound函数的签名特别相似,但是comp函数是不同的!!lower_bound要求的comp是cmp(*iterator,val)->bool 而upper_bound要求的comp是cmp(val,*iterator)->bool 所以这两个cmp的两个参数顺序是相反的!!