- 常量迭代器
- 编译期与运行期
- 异常
- RVO与move
- 完美转发实现与缺陷
- 补充 advance实现,函数带引用修饰 void work() & {}; void work &&{};swap实现,static_assert()实现, vector< bool > 和bitset区别,补充 :stl std::copy,trivially–http://www.fx114.net/qa-70-160551.aspx
常量迭代器,reverse_iterator 实现
优先使用const_iterator, const iterator 与const_iterator 区别
一个是自身不能更改,另一个是指向的对象不能更高
vector<int>::const_iterator cit; // 指向常量的迭代器, cit本身可变, 但*cit不能变
for(cit = v.begin(); cit != v.end(); cit++)
{
cout << *cit << endl;
}
vector<int>::iterator const xit; // error, const iterator自身不能改变, 所以必须对xit进行初始化
const vector<int>::iterator yit; // error, 同上
const_iterator 是一种类型在vector MINGWgcc 4.9.2的实现中是
//in file stl_vector.h
template <typename _Tp, typename _Alloc = std::allocator<_Tp>>
class vector :protected _Vector_base<_Tp, _Alloc>
{
...
typedef __gnu_cxx::alloc_traits<_Tp_alloc_type> _Alloc_traits;
public:
typedef typename _Alloc_traits::const_pointer const_pointer;
typedef __gnu_cxx::_normal_iterator<const_pointer, vector>
const_iterator;
...
};
//in file alloc_traits.h
template<typename _Alloc>
struct __alloc_traits
#if __cplusplus >= 201103L
:std::allocator_tratits<_Alloc>
#endif
{
...
typedef std::allocator_traits<_Alloc> _Base_type;
typedef typename_Base_type::const_pointer const_pointer;
};
//in alloc_traits.h
template<typename _Alloc>
struct allocator_traits
{
_GLIBCXX_ALLOC_TR_NESTED_TYPE(const_pointer,
typename pointer_traits<pointer>::template rebind<const value_type>)
typedef __const_pointer const_pointer;
...
};
#define _GLIBCXX_ALLOC_TR_NESTED_TYPE(_NTYPE, _ALT)\
private:\
template<typename _Tp>\
static typename _Tp::NTYPE _S_##_NTYPE$$_helper(_Tp*);\
static _ALT _S_##_NTYPE##)helper(...);\
typedef decltype(_S_##_NTPE##_helper(_Alloc*)0) __##_NTYPE;
//in ptr_traits.h
template<typename _Ptr>
struct pointer_traits:__ptrtr_pointer<_Ptr>
{
template<_Up>
using rebind = _Up*;
};
//in stl_itertaor
template<tpename _Tterator, typename _Container>
class __normal_itertaor
{
...
};
上述宏_GLIBCXX_ALLOC_TR_NESTED_TYPE用来判断类型是否存在。
使用 C++ SFINAE(替换失败并非错误)特性,对于一个函数模板如果有重载,会先调用更匹配的版本,如果模板不匹配并不产生编译错误而会继续提升类型以匹配重载版本。但是如果模板参数和返回值都匹配成功则不会反过来寻找其他合适的版本,此时如果内部有错误则会报编译错误。
参考:
https://www.zhihu.com/question/55117303/answer/142842404
使用enable_if 判断
//inc++ 11
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true,T> {typedef T type;}
//in c++ 14
template<bool B, class T=void>
using enable_if_t = typename enable_if<B,T>::type;
reverse_itertaor 也是一个类型,不过是itertaor 的adaptor 实现了++,–, begin(),end()等等。。
编译期与运行期
int a =8;
const int ca = a;
std::array<int,ca> a2;//编译出错 ca必须直接初始化 ca= 2;
特别的此处直接写数组
int b[ca];//编译出错 ,ca 不是cosntexpr
//相对于 const constexpr几乎相当于字面量
//区别const 用变量来初始化 不能用于编译期,constexpr可以
//strlen() 非常特殊 本身不是constexpr但是由于用得多编译器作了特殊优化 可以 constexpr auto d = strlen("sasa");
//其他运行期函数一般不能 变为constexpr。 只有在函数定义时 加上constexpr 修饰函数返回值 表明是一个编译期函数就可以。
constexpr int func(cosnt int& c){
return c+ 1;
}
constexpr auto a = 5;
constexpr auto c = func(a);//可以通过编译
结论 :函数有可能用在编译期就应该加上constexpr.因为加上constexpr可以用在编译期,也可以用在运行期,由编译器自动根据需要选择。
C++ 11 中constexpr在函数体内部只能包含一个return语句,可用性边界在于函数体内部从头到尾都是constexpr就可以,超出边界就不可以 。C++ 14放宽了条件,都可以。
C++ 11 中返回值类型为void不能做constexpr,C++ 14 可以。
构造函数声明为constexpr 可以创建 constexpr 对象
但是对于调用constexpr 成员函数由于需要一个this指针对象不能构造为常量类型。
class ConstexprTest {
public:
Constexpr constexprTest(int i = 0):_data(i){};
Constexpr int call() {return _data;}//可以
Constexpr int call_1() {cout <<"" << endl; return _data;}//编译失败
int normal_call() {return _data;}
};
const 对象只能调用const成员函数。
加了constexpr 就可以用于编译期。
和static 完全不是一回事,static虽然也是编译期可知但是,生命周期,存储位置完全不一样。static 和胃初始化的全局变量Bss段,初始化的全局变量放在数据段,堆栈也在数据段,文本段只读放程序文本,局部变量运行时产生。
const int *p =nullptr; \\pointer to const;
constexpr int *p = nullptr; \\const pointer, 编译时就展开了
override和异常
指针知名调用域则可以去除多态性
p->Base::dowok() 不管 obj是不是基类 都会调用 Base::dowork()
掩盖违反了is-a 所以要避免掩盖
在vs2017中虚继承 即使 子类虚函数定义为private 多态仍能正常调用。只要基类是public: 就可以。
虚函数中仅有返回值不同会构成编译错误
补充:函数带引用修饰 void work() & {}; void work &&{};???????? 调用方法的是一个左值,还是一个右值
override 和final 均属于保留字但不是关键字 可以拿来命名变量,函数。。。
final 可以用来阻止虚函数改写,类继承
=noexcept
绝不要抛出一个内建对象作为异常 int ,[], char*,float等
继承一个exception 类测试是否会发生多态?答案会,可以override what 方法, catch(exception &e) cout << e.what() << endl; catch 基类但是调用子类what.
void f() throw(exceptiontype)//此处修饰词 {//编译器一般不会检查括号内的异常类型,只要为空一般认为不抛异常。 c++ 11 中应该使用noexcpt
exceptiontype e1;
throw(e1);//真正写code中不会直接throw()
}
补充: 栈开解noexcept意味着可能会有栈开解。 throw()修饰词, 如果空时抛出异常 意味着肯定栈开解 。noexcept留下了优化空间。??????
noexcept唯一一个带逻辑的饰词。noexcept(条件)
补充swap实现??????static_assert()??????????
析构函数一定是noexcept,默认有noexcept
明确有异常 noexcept(false)肯定会发生异常
可加可不加时不加noexcept,对客户代码影响较小。noexcept 不构成重载。
=delete//成员函数,自由函数均可以,模板也可以,用来禁止特化
可以用在函数后面 避免隐式转换。
int f(int x) {}
int f(char) = delete ;防止char隐式转换
template <typename T>
void ptrProcess(T* ptr)
本能对void* char* 作特化。
如果不需要可以用delete
template<>
void ptrProcess(void* ptr) = delete;
特别对类内部模板非常有效,(层级压制不管用)。
异常参考:
http://blog.csdn.net/caroline_wendy/article/details/17498665
http://blog.csdn.net/earbao/article/details/42240319
栈开解
http://www.jb51.net/article/78644.htm
http://blog.csdn.net/zyq522376829/article/details/46776805
RVO与move语义
当代编译器中RVO return value optimization 默认开启即对于右值返回不会创建更多临时变量只是将控制权转移。但是当函数返回有多个不同条件时不会开启RVO
if xxx:
return A
else:
returb B//不会有RVO
所谓的左值与右值
编译器角度:
凡是真正存在内存中的而不是存在寄存器中的值就是左值。
其余都是右值
临时对象引用必须用const 来引用,但是并不意味着这个对象本身不能改变
class Item {};
void process(const Ite& it) {
cout << "pass by ref" <<endl;
}
process(item())//此时可以调用成功,若函数定义把const 去掉则不能调用,编译失败
cout << item(100).set(1000).get() <<endl;//改变临时变量:
通用引用,折叠规则
右值不会隐式转为左值 需要显式提供一个右值引用重载版本
通用引用可以解决此为题
template<typename T> void process(T&& t ) {} //遇左则左,遇右则右
这样会遇到一个问题:
template<typename T> void process(T&& t ) {
std::vector<T> vec;
...
}
process(100);//此时传递一个右值引用 int&& 给process int && && ->可以折叠为 int&& 此时 T 推导为 int 没有问题
int x;
process(x);//此时传递一个左值引用 int& 给process int& && -> 根据折叠规则 & 无法抵消,T 推导为 int&.此时 std::vector<int&> v 出错;这也是为什么要有完美转发remove_reference
原因
上说 vector 中不能存放引用的原因是:引用不支持一般意义上的赋值操作,而 vector中元素的两个要求是:
1.元素必须能赋值
2.元素必须能复制
move 语义
template <class T>
typename std::remove_reference<T>::type&&
move(T&& arg){
return static_cast<remove_reference<decltype(arg)>::type&&>(arg)
}
参考前面文章和:
http://blog.csdn.net/liyongofdm/article/details/7667942
完美转发实现与缺陷
没有 &&拷贝构造,move会有默认调用普通拷贝构造。
不要重载通用引用函数
注意array不会有move操作,是一个静态的数组无法move
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_referece<_Tp>::type& _t)noexcept{
return static_cast<_Tp&&>(_t);
}
template <typename _Tp>
constexpr _Tp&&
forward(typename std::remove_referece<_Tp>::type&& __t)noexcept{
satic_assert(!std::islvaue_referece<_Tp>::value,"template argument substituting is an lvalue reference type");
return satic_cast<_Tp&&>(__t);
}
缺点 1.初始化列表
1.初始化列表 {...}
void funcImp(const vector<int&> t){
cout << v.size() << endl;
}
template<typename... Ts>
void funcProxy(Ts&&... params)
{
funcImpl(forward<Ts>(params)...);
}
int main(){
f({1,2,3});//没有问题
funcProxy({1,2,3});//编译器仅仅根据{}初始化列表无法推导出为vector<int>
//可以
auto li = {1,2,3};
funcProxy(li);//
}
2.0或NULL作为空指针
0或者NULL作为空指针 完美转发仅会将其认为int。
3.仅声明的静态const成员变量
class Item{
public:
staic const int _val = 28;
};
void funcImpl(int i) {
cout <<i<< endl;
}
template <typename void>funcProxy(T&& t){
funcImpl(forward<T>(t));
}
int main()P
{
funcImpl(Item::_val);//可以
funcProxy(Item::_val);//链接失败,Item::_val没有符号表,已经优化掉了,被展开使用了。类型推导必须在内存中有名字的变量。无法推导
}
//改进方法,不在类内部初始化,在类外部初始化就有一个名字了。可以进行类型推导了。
const int Item::_val = 28;
4.重载的函数或模板
对一个函数做了重载转发的话会找不到原型,必须强制类型转化才可以。using fp = int(*)(int);
5.按位分配的成员变量
struct IPV4Header{
int version:4;
int length:16;
};
void funcImpl(int i) {
cout << i <<endl;
}
template <typename T> funcProxy(T&& t) {
funcImpl(forward<T>(t));
}
int main(){
IPV4Header h;
h.vesion = 1;
h.length = 132;
funcImpl(h.length);//没问题
funcProxy(h.length);//对域操作无法推导出类型
//解决方法只能强制转换
auto length = static_cast<unsigned short> (h.length);
funcProxy(length);
}
补充 advance实现,函数带引用修饰 void work() & {}; void work &&{};swap实现,static_assert()实现, vector< bool > 和bitset区别,补充 :stl std::copy,trivially
advance声明
template <class InputIterator, class Distance>
void advance (InputIterator& it, Distance n);
头文件 #include <iterator>
用法
/ advance example
#include <iostream> // std::cout
#include <iterator> // std::advance
#include <list> // std::list
int main () {
std::list<int> mylist;
for (int i=0; i<10; i++) mylist.push_back (i*10);
std::list<int>::iterator it = mylist.begin();
std::advance (it,5);
std::cout << "The sixth element in mylist is: " << *it << '\n';
return 0;
}
实现GNU4.9.2
template<typename _InputIterator, typename _Distance>
inline void
advance(_InputIterator& __i, _Distance __d)
{
typename iterator_traits<_InputIterator>::difference_type __d = __n;
__advance(__i, __d, std::iterator_category(__i));
}
//接着调用__advace()的不同特化版本,有三个
1. input_iterator_tag
2. bidirectional_iterator_tag
3. random_access_iterator_tag
函数带引用修饰 void work() & {}; void work &&{};
左值引用对象调用& 右值引用对象调用&&
http://tieba.baidu.com/p/2886228636 带补充。。。
http://www.cnblogs.com/Philip-Tell-Truth/p/6370019.html
swap声明
non-array (1)
template <class T> void swap (T& a, T& b)
noexcept (is_nothrow_move_constructible<T>::value && is_nothrow_move_assignable<T>::value);
array (2)
template <class T, size_t N> void swap(T (&a)[N], T (&b)[N])
noexcept (noexcept(swap(*a,*b)));
头文件#include <utility>
使用
// swap algorithm example (C++11)
#include <iostream> // std::cout
#include <utility> // std::swap
int main () {
int x=10, y=20; // x:10 y:20
std::swap(x,y); // x:20 y:10
int foo[4]; // foo: ? ? ? ?
int bar[] = {10,20,30,40}; // foo: ? ? ? ? bar: 10 20 30 40
std::swap(foo,bar); // foo: 10 20 30 40 bar: ? ? ? ?
std::cout << "foo contains:";
for (int i: foo) std::cout << ' ' << i;
std::cout << '\n';
return 0;
}
实现类似
template <class T> void swap (T& a, T& b)
{
T c(std::move(a)); a=std::move(b); b=std::move(c);
}
template <class T, size_t N> void swap (T &a[N], T &b[N])
{
for (size_t i = 0; i<N; ++i) swap (a[i],b[i]);
}
参考:
http://en.cppreference.com/w/cpp/algorithm/swap
static_assert()
静态断言在编译期判断否则产生一个编译错误
http://www.cnblogs.com/lvdongjie/p/4489835.html
使用
`#include
template
void swap(T& a, T& b)
{
static_assert(std::is_copy_constructible::value,
“Swap requires copying”);
static_assert(std::is_nothrow_move_constructible::value
&& std::is_nothrow_move_assignable::value,
“Swap may throw”);
auto c = b;
b = a;
a = c;
}
template
struct data_structure
{
static_assert(std::is_default_constructible::value,
“Data Structure requires default-constructible elements”);
};
struct no_copy
{
no_copy ( const no_copy& ) = delete;
no_copy () = default;
};
struct no_default
{
no_default () = delete;
};
int main()
{
int a, b;
swap(a, b);
no_copy nc_a, nc_b;
swap(nc_a, nc_b); // 1
data_structure<int> ds_ok;
data_structure<no_default> ds_error; // 2
}`
结果
1: error: static assertion failed: Swap requires copying
2: error: static assertion failed: Data Structure requires default-constructible elements
vector< bool > 和bitset区别
bool arr[] = {1, 0, 0, 1, 1};
bool arr2[] = {0, 1, 0, 1, 1};
vector<bool> v1(arr, arr+5), v2(arr2, arr2+5);
vector<bool> v3(v1.size());
std::transform(v1.begin(), v1.end(), v2.begin(),
v3.begin(), std::logical_and<int>());
copy(v3.rbegin(), v3.rend(), ostream_iterator<bool>(cout));
http://blog.csdn.net/lanchunhui/article/details/49647933
为什么避免使用vector<bool>
http://blog.csdn.net/yongjunhe/article/details/8137248
http://www.tuicool.com/articles/Izy6Vb
std::copy
声明
template< class InputIt, class OutputIt >
OutputIt copy( InputIt first, InputIt last, OutputIt d_first );
(1)
template< class ExecutionPolicy, class ForwardIt1, class ForwardIt2 >
ForwardIt2 copy( ExecutionPolicy&& policy, ForwardIt1 first, ForwardIt1 last, ForwardIt2 d_first );
(2) (since C++17)
template< class InputIt, class OutputIt, class UnaryPredicate >
OutputIt copy_if( InputIt first, InputIt last,
OutputIt d_first,
UnaryPredicate pred );
(3) (since C++11)
template< class ExecutionPolicy, class ForwardIt1, class ForwardIt2, class UnaryPredicate >
ForwardIt2 copy_if( ExecutionPolicy&& policy, ForwardIt1 first, ForwardIt1 last,
ForwardIt2 d_first,
UnaryPredicate pred );
(4) (since C++17)
头文件#include <algorithm>
实现
template<class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last,
OutputIt d_first)
{
while (first != last) {
*d_first++ = *first++;
}
return d_first;
}
template<class InputIt, class OutputIt, class UnaryPredicate>
OutputIt copy_if(InputIt first, InputIt last,
OutputIt d_first, UnaryPredicate pred)
{
while (first != last) {
if (pred(*first))
*d_first++ = *first;
first++;
}
return d_first;
}
使用;
#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>
int main()
{
std::vector<int> from_vector(10);
std::iota(from_vector.begin(), from_vector.end(), 0);
std::vector<int> to_vector;
std::copy(from_vector.begin(), from_vector.end(),
std::back_inserter(to_vector));
// or, alternatively,
// std::vector<int> to_vector(from_vector.size());
// std::copy(from_vector.begin(), from_vector.end(), to_vector.begin());
// either way is equivalent to
// std::vector<int> to_vector = from_vector;
std::cout << "to_vector contains: ";
std::copy(to_vector.begin(), to_vector.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}
trivially
有没有都一样
http://www.fx114.net/qa-70-160551.aspx