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;
};