跟我学c++中级篇——完美转发的异常情况

236 篇文章 96 订阅

一、完美转发

c++11的完美转发即std::forward可以说是一个非常不错的库应用,在现在的c++代码中,多多少少几乎都可以看到它的影子。在前面的代码也分析过它的一些特点,提到了它应用的目的和实际的作用。需要说明的它是使用右值引用这个号称万能引用的符号来实现值类型和cv限定符的确定性转发,它意味着值传递(副本传递)不在这个讨论范围内的。
顺带也提到了它有一些情况是会产生转发失败的。下面就对转发失败的情况下进行一下分析。

二、一些特殊情况

正常情况下的完美转发的样子大家看得很多,一般诸如下面这样:

template<typename T>
void fn(T&& t)  
{
   f(std::forward<T>(t));  
}

在std::forward中,失败的情况其实就是完美转发的目的没有达到,甚至根本无法实现。
可以分为以下几类:
1、花括号的初始化方式调用

#include <iostream>
#include <array>

void test(const std::array<int,5> &arr){}

template<typename T>
void fn(T&& t)
{
    test(std::forward<T>(t));
}
int main()
{
    std::array<int, 5> arr = {0,1,2};
    fn(arr);
   // fn({0,1,2,3,4});
}

如上面注释的部分,想省敲几下键盘,编译就过不去,报一个“no matching function for call to ‘fn()’”的异常。

2、0或者NULL做为空指针传递
其实这个也是c++11后强调使用nullptr做为指针的空值的一个重要原因。之所以会这样,是因为0和NULL往往会被默认转成为int类型,这也是在早期的C编译器中经常遇到的一个编译现象,就是“XXX无法转成int”其实就是写错了,但默认就是往int上靠,这个没办法。一如下面:

void test( void *ptr){}

template<typename T>
void fn(T&& t)
{
    test(std::forward<T>(t));
}
int main()
{
    fn(0);
}

上面的代码都不会调用成功的。当然,如果用在模板编程中,这个现象会更让人觉得酸爽。

3、static const的应用(含constexpr)
这个有点小不同,先看下代码:

void test(int i){}

class Data {
public:
    static const int d_ = 1;
};
//const  int Data::d_ ;

template<typename T>
void fn(T&& t)
{
    test(std::forward<T>(t));
}
int main()
{
    fn(Data::d_);
}

这段代码在VS中可以成功运行,但在g++中会报一个链接错误“ undefined reference to Data::d_ collect2: error: ld returned 1 exit status”。VS中对编译和链接相对来说还是宽松一些。
想通过连接只需要把注释的部分解开就可以了。

4、对重载和模板的函数名处理
这句话的意思是指在完美转发时如果参数是一个函数名称,那么如果这个函数名称的函数如果存在重载(或是模板)的话,在普通编程的直接调用情况下是没有问题,但是在完美转发时,不管是直接调用名称还是使用模板函数时会存在转发的错误。看下面的代码就理解了。

void myfunc_test(void func(int)) {}

void myfunc(int a) {}
void myfunc(int a, int b) {}

template<typename T>
void dotest(T t){}
template<typename T>
void fn(T&& t)
{
    myfunc_test(std::forward<T>(t));
}

int main()
{
   // fn(myfunc);
   // fn(dotest);
    myfunc_test(myfunc);
}

一般来说,传递函数做为函数参数时,用函数指针的情况很多,但也应该知道,也可以直接传递函数名称而不是指针的情况来操作。上面的代码就是这样,就会出现本节的问题。但是如果直接调用函数myfunc_test而非完美转发,则没有问题。同样,函数模板也是如此,因为函数模板毕竟不是一个实例,而是一组类似实例的泛型。
想要解决这个问题也很简单,依照着普通编译成功的方式,采用指针的方式直接指明调用的函数即可,模板也同样如此。

5、位字段
这个就没有更具体的可聊的了,毕竟c++的标准确定了non-const引用不能绑定到位字段。解决的方法也很容易,其实不能算解决是绕过,即通过一个转化的副本来实现即可。这里就不再举例了。

三、总结

前面不是提到过,引入新标准就会有新的问题,这个完美转发就是一个例子。这里就看利弊的取舍和应用了,完美转发的实际应用情况已经回答了这个问题。至于在以后的标准中会不会有什么对些完善和更新的,只有看标准的发展了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值