C++11编译器优化以及引用折叠
1.左值与右值的意义
1.左值引用和右值引用最终目的是减少拷贝,提高效率
2.左值引用还可以修改参数/返回值
左值引用不足:
部分函数放回场景,只能传值返回,不能引用左值返回
当前函数局部对象,出了当前函数作用域生命周期到了,就销毁了,不能引用左值引用返回,只能传值返回
解决方案一:不用返回值,用输出型参数(牺牲可读性)
解决方案二:编译器优化
解决方案三:右值引用和移动语义
代码示例
把创建的对象变成参数传过去,形参是引用的方式接受,就可以减少拷贝,提高效率。
#include<stdio.h>
#include<vector>
#include<iostream>
using namespace std;
class Solution {
public:
// 这里的传值返回拷贝代价就太大了
/*vector<vector<int>> generate(int numRows) {
vector<vector<int>> vv(numRows);
for (int i = 0; i < numRows; ++i)
{
vv[i].resize(i + 1, 1);
}
for (int i = 2; i < numRows; ++i)
{
for (int j = 1; j < i; ++j)
{
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
return vv;
}*/
void generate(int numRows, vector<vector<int>>& vv) {
//vector<vector<int>> vv(numRows);
vv.resize(numRows);
for (int i = 0; i < numRows; ++i)
{
vv[i].resize(i + 1, 1);
}
for (int i = 2; i < numRows; ++i)
{
for (int j = 1; j < i; ++j)
{
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
for (auto& s : vv)
{
for (auto& ss : s)
{
cout << ss;
}
cout << endl;
}
}
};
int main()
{
//vector<vector<int>> ret = Solution().generate(100);
vector<vector<int>> ret;
Solution().generate(100, ret);
/*bit::string ret;
Solution().addStrings("1111111", "222222", ret);*/
return 0;
}
2.右值对象构造,只有拷贝构造,没有移动构造的场景
下图第一个是在Linux下用指令实现的不优化的场景,先是参数的传过去发生构造,然后把形参进行拷贝构造,俩个参数会有俩次,而后是返回值要拷贝给临时变量有一次拷贝构造,临时变量给ret又会发生拷贝构造。
第二个是1代优化,编译器会有优化,把构造和拷贝构造合成构造,企鹅在返回时不生成临时变量,直接给ret拷贝构造。
第三个是二代优化,连临时变量都不生成,把ret对象的引用传过去,大大提高了效率。
对于赋值的情况
第一个是无优化情况,是先拷贝给临时变量然后在拷贝复制给ret
第二个是一代优化的情况,跟无优化的一样
第三个是二代优化,可以看到只剩拷贝赋值了,把拷贝构造和拷贝赋值合成一个,生成的临时变量是返回对象的引用,就可以减少拷贝构造了
3.有移动构造和移动赋值后
不优化情况,虽然有很多的移动构造存在,但是移动构造的代价很少
一代优化情况下,就是直接返回值对ret进行移动构造,不要临时变量
二代优化情况下,就是str是ret对象的引用,就不会有俩次移动构造
有赋值情况下
不优化情况下是有构造,移动构造和移动赋值
一代优化情况下和不优化是一样的
二代优化是把str变成临时变量的引用,这样就可以省略掉移动构造,这里的临时对象是会有一次构造的
总结
在C++11之前的操作都是非标准的,在C++11后,有了移动构造和移动赋值,前面的优化操作就是锦上添花了,对于深拷贝的传值返回,有了移动构造和移动赋值代价就很小了,而对移动构造和移动赋值进行优化,提升是很小的,可以舍弃的。
4.引用折叠
C++中不能直接定义引用的引用如int& && r=i;这样写会报错,通过模板或typedef中的类型操作可以构成引用的引用。
通过模板或typedef中的类型操作构成引用时,这时C++11给出了一个引用折叠的规矩:右值引用的右值引用折叠成右值引用,其它组合都是左值引用。
示例代码
// 由于引用折叠限定,f1实例化以后总是一个左值引用
template<class T>
void f1(T& x)
{}
// 由于引用折叠限定,f2实例化后可以是左值引用,也可以是右值引用
template<class T>
void f2(T&& x)
{}
int main()
{
typedef int& lref;
typedef int&& rref;
int n = 0;
// 引用折叠
lref& r1 = n; // r1 的类型是 int&
lref&& r2 = n; // r2 的类型是 int&
rref& r3 = n; // r3 的类型是 int&
rref&& r4 = 1; // r4 的类型是 int&&
// 没有折叠->实例化为void f1(int& x)
f1<int>(n);
//f1<int>(0); // 报错
// 折叠->实例化为void f1(int& x)
f1<int&>(n);
//f1<int&>(0); // 报错
// 折叠->实例化为void f1(int& x)
f1<int&&>(n);
//f1<int&&>(0); // 报错
// 折叠->实例化为void f1(const int& x)
f1<const int&>(n);
f1<const int&>(0);
// 折叠->实例化为void f1(const int& x)
f1<const int&&>(n);
f1<const int&&>(0);
// 没有折叠->实例化为void f2(int&& x)
//f2<int>(n); // 报错
f2<int>(0);
// 折叠->实例化为void f2(int& x)
f2<int&>(n);
//f2<int&>(0); // 报错
// 折叠->实例化为void f2(int&& x)
//f2<int&&>(n); // 报错
f2<int&&>(0);
return 0;
}
万能引用
既可以变成左值引用也可以变成右值引用
万能引用
template<class T>
void Function(T&& t)
{
int a = 0;
T x = a;
//x++;
cout << &a << endl;
cout << &x << endl << endl;
}
int main()
{
// 10是右值,推导出T为int,模板实例化为void Function(int&& t)
//Function(10);
int a;
// a是左值,推导出T为int&,引用折叠,模板实例化为void Function(int& t)
//Function(a); // 左值
// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)
//Function(std::move(a));
const int b = 8;
// b是左值,推导出T为const int&,引用折叠,模板实例化为void Function(const int& t)
// 所以Function内部会编译报错,x不能++
//Function(b); // const 左值
// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&& t)
// 所以Function内部会编译报错,x不能++
Function(std::move(b)); // const 右值
return 0;
}
5.完美转发
function(T&& t)函数模板中,传左值实例化后是左值引用的function函数,传右值实例化以后是右值引用的function函数
前面知识可知,变量表达式都是左值属性,也就意味着一个右值被右值引用绑定后,右值引用变量表达式的属性是左值,在function函数中t的属性是左值,那么把t传递下一层函数,这时匹配的就是左值引用的版本的函数,要保证t对象的属性,就需要使用完美转发来来实现。
代码示例
foword<T>()可以保持对象的属性,如果没有则就不会有一个右值打印出来,因为右值引用会有左值属性
void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }
template < class T>
void Function(T&& t)
{
Fun(forward<T>(t));
}
int main()
{
// 10是右值,推导出T为int,模板实例化为void Function(int&& t)
Function(10); // 右值
int a;
// a是左值,推导出T为int&,引用折叠,模板实例化为void Function(int& t)
Function(a); // 左值
// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)
Function(std::move(a)); // 右值
const int b = 8;
// a是左值,推导出T为const int&,引用折叠,模板实例化为void Function(const int&t)
Function(b);
// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&&t)
Function(std::move(b)); // const 右值
return 0;
}
6.补充
把完美转发引入到自定义list中,模板X会自动推导类型出来。
Lish.h
#pragma once
#include<assert.h>
//#include"Iterator.h"
namespace bit
{
template<class T>
struct list_node
{
T _data;
list_node<T>* _next;
list_node<T>* _prev;
/*list_node(const T& data = T())
:_data(data)
,_next(nullptr)
,_prev(nullptr)
{}
list_node(T&& data)
:_data(forward<T>(data))
, _next(nullptr)
, _prev(nullptr)
{}*/
list_node() = default;
template<class X>
list_node(X&& data)
:_data(forward<X>(data))
, _next(nullptr)
, _prev(nullptr)
{}
};
template<class T, class Ref, class Ptr>
struct list_iterator
{
typedef list_node<T> Node;
typedef list_iterator<T, Ref, Ptr> Self;
Node* _node;
list_iterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
/*template<class T>
struct list_const_iterator
{
typedef list_node<T> Node;
typedef list_const_iterator<T> Self;
Node* _node;
list_const_iterator(Node* node)
:_node(node)
{}
const T& operator*()
{
return _node->_data;
}
const T* operator->()
{
return &_node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};*/
template<class T>
class list
{
typedef list_node<T> Node;
public:
/*typedef list_iterator<T> iterator;
typedef list_const_iterator<T> const_iterator;*/
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
//typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
//typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
//reverse_iterator rbegin()
//{
// return reverse_iterator(--end());
//}
//reverse_iterator rend()
//{
// return reverse_iterator(end());
//}
// 对称
/* reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
}*/
iterator begin()
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator begin() const
{
return _head->_next;
}
const_iterator end() const
{
return _head;
}
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
list(initializer_list<T> il)
{
empty_init();
for (auto& e : il)
{
push_back(e);
}
}
// lt2(lt1)
list(const list<T>& lt)
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
}
// lt1 = lt3
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
auto it = begin();
while (it != end())
{
it = erase(it);
}
}
// 16:18继续
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
/*void push_back(const T& x)
{
insert(end(), x);
}
void push_back(T&& x)
{
insert(end(), forward<T>(x));
}*/
// 万能引用
template<class X>
void push_back(X&& x)
{
insert(end(), forward<X>(x));
}
void push_front(const T& x)
{
insert(begin(), x);
}
//iterator insert(iterator pos, const T& x)
//{
// Node* cur = pos._node;
// Node* prev = cur->_prev;
// Node* newnode = new Node(x);
// // prev newnode cur
// newnode->_next = cur;
// cur->_prev = newnode;
// newnode->_prev = prev;
// prev->_next = newnode;
// ++_size;
// return newnode;
//}
//iterator insert(iterator pos, T&& x)
//{
// Node* cur = pos._node;
// Node* prev = cur->_prev;
// Node* newnode = new Node(forward<T>(x));
// // prev newnode cur
// newnode->_next = cur;
// cur->_prev = newnode;
// newnode->_prev = prev;
// prev->_next = newnode;
// ++_size;
// return newnode;
//}
template<class X>
iterator insert(iterator pos, X&& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(forward<X>(x));
// prev newnode cur
newnode->_next = cur;
cur->_prev = newnode;
newnode->_prev = prev;
prev->_next = newnode;
++_size;
return newnode;
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
--_size;
return next;
}
size_t size() const
{
return _size;
}
bool empty() const
{
return _size == 0;
}
private:
Node* _head;
size_t _size;
};
struct AA
{
int _a1 = 1;
int _a2 = 1;
};
按需实例化
T* const ptr1
const T* ptr2
//template<class Container>
//void print_container(const Container& con)
//{
// // const iterator -> 迭代器本身不能修改
// // const_iterator -> 指向内容不能修改
// typename Container::const_iterator it = con.begin();
// //auto it = con.begin();
// while (it != con.end())
// {
// //*it += 10;
// cout << *it << " ";
// ++it;
// }
// cout << endl;
// for (auto e : con)
// {
// cout << e << " ";
// }
// cout << endl;
//}
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<vector>
#include<iostream>
#include<map>
#include<string>
#include<assert.h>
using namespace std;
#include<list>
#include"Lish.h"
namespace bit
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
cout << "string(char* str)-构造" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// 拷贝构造
string(const string& s)
:_str(nullptr)
{
cout << "string(const string& s) -- 拷贝构造" << endl;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
void swap(string& ss)
{
::swap(_str, ss._str);
::swap(_size, ss._size);
::swap(_capacity, ss._capacity);
}
// 移动构造
string(string&& s)
{
cout << "string(string&& s) -- 移动构造" << endl;
// 转移掠夺你的资源
swap(s);
}
string& operator=(const string& s)
{
cout << "string& operator=(const string& s) -- 拷贝赋值" <<
endl;
if (this != &s)
{
_str[0] = '\0';
_size = 0;
reserve(s._capacity);
for (auto ch : s)
{
push_back(ch);
}
}
return *this;
}
// 移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
swap(s);
return *this;
}
~string()
{
//cout << "~string() -- 析构" << endl;
delete[] _str;
_str = nullptr;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
if (_str)
{
strcpy(tmp, _str);
delete[] _str;
}
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity *
2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
const char* c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
};
}
class Solution {
public:
// 传值返回需要拷贝
bit::string addStrings(bit::string num1, bit::string num2) {
bit::string str;
int end1 = num1.size() - 1, end2 = num2.size() - 1;
// 进位
int next = 0;
while (end1 >= 0 || end2 >= 0)
{
int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
int ret = val1 + val2 + next;
next = ret / 10;
ret = ret % 10;
str += ('0' + ret);
}
if (next == 1)
str += '1';
reverse(str.begin(), str.end());
//cout << &str << endl;
return str;
}
};
int main()
{
bit::list<bit::string> lt;
bit::string s1("11111111111");
lt.push_back(s1);
bit::string s2("33333333333");
lt.push_back(move(s2));
lt.push_back("22222222222");
}