完美转发函数

这篇博客探讨了C++中的完美转发在构造函数中的应用,以及如何通过模板和std::enable_if禁用特定类型的构造。文章通过示例展示了如何使用模板构造函数和拷贝/移动构造函数,并解释了为何模板构造函数不能作为拷贝构造函数的替代。同时,还介绍了如何利用模板别名简化代码。
#include <iostream>

class Person{
public:

    // 完美转发用于构造函数
    template<typename STR>
    Person(STR&& n):name(std::forward<STR>(n)) {
        std::cout<<"tmpl-conster"<<std::endl;
    }

    Person(const Person& p):name(p.name){
        std::cout<<"copy constructor"<<std::endl;
    }
    Person(Person&& p): name(std::move(p.name)){
        std::cout<<"move constructor"<<std::endl;
    }
private:
    std::string name;
};

void Test(){
    std::string str="hello";
    Person p(str); // tmpl-conster

    Person p1("tmp");// tmp-conster;

    // error, 模板构造函数copy constructor 函数(有const限定符)比更适合
    // 但模板构造函数不是拷贝构造函数实现,所以报错,当然在添加个非const拷贝
    // 构造函数可以解决报错,最好处理时禁用模板构造函数,后调用带有const的万能引用拷贝构造函数
    /*
        Person(Person& p):name(p.name){}
    */
    // Person p2(p1);

    Person p2(std::move(p1)); // move constructor
}



template<typename T>
using EnableIfString = typename std::enable_if<std::is_convertible<T, std::string>::value>::type;


class Person1{
public:

    //若STR类型到std::string不是可转换,则禁用模板函数

    // 写法太复杂,可以使用模板别名
    //template<typename STR, typename=typename std::enable_if<std::is_convertible<STR, std::string>::value>::type>
    template<typename STR, typename=EnableIfString<STR>> // EnableIfString使用更简易
    Person1(STR&& n):name(std::forward<STR>(n)) {
        std::cout<<"tmpl-conster"<<std::endl;
    }

    Person1(const Person1& p):name(p.name){
        std::cout<<"copy constructor"<<std::endl;
    }
    Person1(Person1&& p): name(std::move(p.name)){
        std::cout<<"move constructor"<<std::endl;
    }
private:
    std::string name;
};

void Test1(){
    Person1 p("tmp");
    Person1 p1(p);// copy constructor, p不是string类型,模板构造函数被禁用,只能调用拷贝构造函数
}

int main(){
    Test();
    Test1();
}
### 完美转发的实现原理 完美转发(Perfect Forwarding)是 C++11 引入的一项语言特性,旨在解决在泛型编程中将参数以原始的值类别(左值、右值、常量等)传递给目标函数的问题。其核心依赖于右值引用(rvalue reference)和 `std::forward` 的结合使用。 右值引用允许函数模板接受参数的原始类型和值类别,而 `std::forward` 则用于在转发过程中保持这些特性。具体来说,当一个函数模板接收一个通用引用(也称为万能引用,如 `T&&`)时,`std::forward<T>(arg)` 会根据 `T` 的类型决定是将参数以左值引用还是右值引用的形式传递给下一个函数。这种机制避免了不必要的拷贝和转换操作,从而提升性能[^2]。 ### 完美转发的代码示例 以下是一个典型的完美转发示例,展示了如何编写一个能够转发任意参数的函数模板: ```cpp #include <iostream> #include <utility> // std::forward // 目标函数,用于接收转发的参数 void target_function(int& x) { std::cout << "Lvalue reference: " << x << std::endl; } void target_function(int&& x) { std::cout << "Rvalue reference: " << x << std::endl; } // 完美转发函数模板 template <typename T> void forward_function(T&& arg) { target_function(std::forward<T>(arg)); } int main() { int a = 42; // 调用转发函数,传递左值 forward_function(a); // 输出 "Lvalue reference: 42" // 调用转发函数,传递右值 forward_function(42); // 输出 "Rvalue reference: 42" return 0; } ``` 在上述代码中,`forward_function` 是一个模板函数,它接受一个通用引用 `T&& arg`。通过 `std::forward<T>(arg)`,它将参数 `arg` 以原始的值类别转发给 `target_function`。如果传入的是左值(如变量 `a`),则调用接受左值引用的 `target_function`;如果传入的是右值(如字面量 `42`),则调用接受右值引用的 `target_function`。 ### 万能引用的识别 在 C++ 中,并非所有的 `T&&` 都是万能引用。万能引用的判断规则是:**只有在模板参数推导的情况下,`T&&` 才被视为万能引用**。根据这一规则,以下选项中属于万能引用的是: - **A. `template<typename T> void f(T&& arg)`** 这是一个典型的万能引用,因为它依赖于模板参数推导,`T` 可以根据传入的参数推导为左值引用或右值引用[^4]。 - **D. `auto&& val = get_value()`** 这也是万能引用的一种形式,`auto&&` 会根据 `get_value()` 的返回类型进行推导,既可以绑定到左值,也可以绑定到右值。 而以下选项则不是万能引用: - **B. `template<typename T> void f(const T&& arg)`** 由于 `const T&&` 明确指定了右值引用,因此它只能绑定到右值,不具备万能引用的特性[^4]。 - **C. `template<typename T> void f(std::vector<T>&& arg)`** 此处的 `std::vector<T>&&` 是具体的右值引用,而不是通用的 `T&&`,因此它也不能作为万能引用[^4]。 ### 总结 完美转发通过结合右值引用和 `std::forward`,使得函数模板能够精确地将参数以原始的值类别传递给目标函数。这种机制在编写通用库代码时尤为重要,能够有效避免不必要的拷贝操作,提升程序性能。正确识别和使用万能引用是实现完美转发的关键步骤之一。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值