条款26:避免万能引用类型进行重载

考虑如下代码:

std::multiset<std::string> names;		// 全局数据结构
void logAndAdd(const std::string &name) {
	auto now = std::chrono::system_clock::now();
	log(now, "logAndAdd");
	names.emplace(name);				// 將名字添加到全局数据结构中
}

std::string petName("Darla");
logAndAdd(petName);					   // 传递左值std::string
logAndAdd(std::string("Persephone"));  // 传递右值std::string
logAndAdd("Patty Dog");				   // 传递右值std::string

在第一个调用中,logAndAdd的形参绑定到了变量petName。在logAndAdd内部,name被传递给了names.emplace,由于name是个左值,所以是被复制进names的。

第2个和第3个调用name绑定到一个右值,由于name是个左值,所以是被复制进names的;我们可以改写logAndAdd让它接受一个万能引用:

template<typename T>
void logAndAdd(T &&name) {
	auto now = std::chrono::system_clock::now();
	log(now, "logAndAdd");
	names.emplace(std::forward<T>(name));
}

std::string petName("Darla");
logAndAdd(petName);					   // 传递左值std::string,將左值复制进multiset
logAndAdd(std::string("Persephone"));  // 对右值进行移动
logAndAdd("Patty Dog");				   // 在multiset中直接构造一个std::string对象,而非复制一个st::string类型的临时对象
}

在考虑如下情景,我们增加一个logAndAdd的重载版本:

typename<typename T>
void logAndAdd(T &&name) {
	auto now = std::chrono::system_clock::now();
	log(now, "logAndAdd");
	names.emplace(std::forward<T>(name));
}

std::string nameFromIdx(int dx);
void logAndAdd(int dx) {
	auto now = std::chrono::system_clock::now();
	log(now, "logAndAdd");
	names.emplace(nameFromIdx(idx));				// 將名字添加到全局数据结构中
}

std::string petName("Darla");
logAndAdd(petName);					// 下面三个掉用都调用了形参为T&&的重载版本
logAndAdd(std::string("Persephone"));  
logAndAdd("Patty Dog");

logAndAdd(22);						// 调用了形参类型为int的版本

但是如果客户产生了如下调用,会无法编译

short nameIndx;
logAndAdd(nameIdx); // !错误

logAndAdd有个两个重载版本,,形参为T&&的版本將T推导为short,从而产生一个精确匹配。而形参为int的版本却只能在类型提升后才能匹配到short形参,但是names无法添加short类型的元素

填上这个坑一个办法是撰写一个带有完美转发的构造函数。

class Person{
public:
    template<typename T>
    explicit Person(T &&n) :name(std::forward<T>(n)){}
    explicit Person(int idx) : name(nameFromIdx(idx)){}
    Person(const Person&& rhs); 		// 编译器生成拷贝构造函数
    Person(Person &&rhs);				// 编译器生成移动构造函数
private:
    std::string name;
};

C++会默认生成拷贝构造和移动构造函数,如果用户产生如下调用:

Person p("Nancy");
auto cloneOfP(p); 

上面代码没有调用拷贝构造函数,而是调用了完美转发构造函数。该函数在尝试从一个Person类型的对象p出发来初始化另外一个Person类型的对象的std::string类型的数据成员。所以编译失败。

完美转发构造函数如果扯上集成后会变得更加复杂:

class SpecialPerson : public Person
{
public:
    SpecialPerson(const SpecialPerson &rhs) : Person(rhs) {}        //拷贝构造函数,调用的是基类的完美构造函数
    SpecialPerson(SpecialPerson &&rhs) : Person(std::move(rhs)){}   //移动构造函数,调用的是基类的完美构造函数
};

最终代码没有通过编译,因为std::string没有接受SpecialPerson类型的形参

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值