第六章 移动语义和enable_if

1.关于完美转发,什么是完美转发,当函数接收一个左值引用,转发给另一个函数时也是一个左值引用

class X {
public:

};
void g(X& x) { //1
	std::cout << "X&" << std::endl;
}
void g(const X& x) {//2
	std::cout << "const X&" << std::endl;
}
void g(X&& x) {//3
	std::cout << "X&&" << std::endl;
}
void f(X&x) {//1
	g(x);
}
void f(const X& x) {//2
	g(x);
}
void f(X&& x) {//3
	g(std::forward<X>(x));
	g(std::move(x));
        g(x);
}
//为什么要使用std::move,因为我们如果调用f(X()),虽然是个右值,但是在表达式中,也就是第三个f中,g(x)中的x类型是X&,侧面验证了,参数类型虽然是&&,但x实际上是X&,所以这就是我们要用std::forward和std::move的原因

2.特别的成员函数模板

	std::string name;
	template<typename T>
	Person(T&& t) :name(std::forward<T>(t)) {
		std::cout << "perfect transform" << std::endl;
	}
	Person(const Person& person) :name(person.name) {
		std::cout << "copy constructor" << std::endl;
	}
	Person(Person&& person) noexcept :name(std::move(person.name)){
		std::cout << "move constructor" << std::endl;
	}
        //当调用Person person("muban");Person person1(person);//error 因为这个拷贝构造会优先调用万能转发的函数,类型不匹配我们将通过std::enable_if来解决这个问题

3.enable_if的基本用法

template<typename T>
typename std::enable_if<(sizeof(T) > 4),T>::type foo() {

}
当sizeof(T)>4为真时,T为返回值的类型,否则不会生成这个类型,也叫做SFINAE

但通常的用法是作为第二个模板参数

template<typename T, typename = std::enable_if_t<(sizeof(T) > 4)>>void Doo() {
	std::cout << "校验成功" << std::endl;
}
//如果sizeof<T>>4不成立,那么编译器就会直接报错

4.修改前面的问题

	template<typename T,typename = std::enable_if_t<std::is_convertible_v<T,std::string>>>
	Person(T&& t) :name(std::forward<T>(t)) {
		std::cout << "perfect transform" << std::endl;
	}

5.优先使用我们自己定义的拷贝构造函数

	P(const volatile P&) = delete;//使用const volatile删除掉默认拷贝构造
	template<typename T>
	P(const T& t) {
		std::cout << "模板拷贝构造函数" << std::endl;
	}

6.实际使用

template<typename T>
class P {
public:
	P(int a) :a(a) {
	}
	P(const volatile P&) = delete;
	template<typename U, typename = std::enable_if_t<std::is_convertible_v<U, P<T>>>>
	P(const P<U>& t) {
		std::cout << "模板拷贝构造函数" << std::endl;
	}
	int a;
};