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

一、完美转发

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

二、一些特殊情况

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

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

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

#include 
#include 

void test(const std::array &arr){}

template
void fn(T&& t)
{
    test(std::forward(t));
}
int main()
{
    std::array 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
void fn(T&& t)
{
    test(std::forward(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
void fn(T&& t)
{
    test(std::forward(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
void dotest(T t){}
template
void fn(T&& t)
{
    myfunc_test(std::forward(t));
}

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

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

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

三、总结

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

你可能感兴趣的:(C++11,C++,c++)