条款30:熟悉完美转发的失败情形

“转发”的含义不过是一个函数把自己的形参传递(转发)给另外一个函数而已。完美转发的含义是我们不仅转发对象,还转发其显著特征:类型,左值,右值,以及是否带有constvolation修饰词等。

假设有某函数f,然后我们呢打算撰写一个函数將f作为转发目标,考虑如下代码:

template<typename T>
void fwd(T && param) {			//接受任意实参
	f(std::forward<T>(param));	//转发到f
}

给定目标函数f和转发函数fwd,当以某特定实参f会执行某操作,而用同一实参调用fwd会执行不同操作,则称完美转发失败:

f(experssion);	// 如果本语句执行某种操作
fwd(experssion);// 而本语句执行了不同的操作,则称fwd完美转发experssion到f失败

以下逐一讲解不能实施完美转发的实参

大括号初始化物

假设f声明如下:

void f(const std::vector<int> &);

f({1, 2, 3});	// 没问题,{1, 2, 3}会隐式转换std::vector<int>

fwd({1, 2, 3}); // 错误! 无法通过编译

上述fwd为大括号初始化物的运用,就是一种完美转发失败的情形;经由转发函数模板fwd来对f实施间接调用时,编译器就不再会比较fwd调用处传入的实参所声明的形参数了。完美转发在如下条件成立时候失败

  1. 编译器无法为一个或多个fwd形惨推导出类型结果
  2. 编译器为一个或多个fwd的形参推导出了“错误的”类型结果

0和NULL用作空指针

条款8说过,若尝试把0和NULL以空指针传递给函数模板,类型推导就会发生行为扭曲,推导结果会是整型。结论就是:0和NULL都不能用作空指针以进行完美转发

仅有声明的整型static const成员变量

考虑如下代码:

class widget {
public:
    static const std::size_t MinVals = 28;		// 给出MinVals声明
};

void f(std::size_t val) {
    cout << "this is f fuxntion";
}

template<typename T>
void fwd(T && param) {			//接受任意实参
    f(std::forward<T>(param));	//转发到f
}

std::vector<int> widgetData;
widgetData.reserve(widget::MinVals);

f(widget::MinVals);				// 没有问题,当f(28)处理
fwd(widget::MinVals);			// 错误,应该无法链接

重载的函数名字和模板名字

考虑如下代码:

int processval(int value) {
    return 0;
}

int processval(int value, int priority) {
    return 1;
}

f(processval);		// 没有问题

上述f调用中,编译器知道它们需要是调用的是哪个processVal。但是fwd就行不行了,因为作为一个函数模板,它没有任何关于类型需求的信息,这也使得编译器不可能决议应该传递哪个函数重载版本

fwd(processVal); 	// 错误!哪个processVal重载版本?

位域

void f(std::size_t sz) {
    std::cout << "this is f function";
}

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

IPv4Header h;
f(h.totalLength);	// 没问题
fwd(h.totalLength); // 错误! 非const引用不得绑定到位域
上一篇:自定义线程池开发实例代码


下一篇:Navigation区域导航