标签分派
一个万能引用形参通常会导致的后果是无论传入了什么都给出一个精确匹配结果,不过,如果万能引用仅是形参列表的一部分,该列表中还有其他非万能引用类型的形参的话,那么只要该非万能引用形参具备充分差的匹配能力,则它足以將这个带有万能引用形参的重载版本剔除出局。举例条款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); // 將名字添加到全局数据结构中
}
本条款的目的是避免条款26的问题,重新实现logAndAdd
, 把它委托另外两个函数,一个接受整形数值,另一个接受其他所有类型。而logAndAdd
本书则接受所有类型的实参:
template<typename T>
void logAndAdd(T &&name) {
logAndAddImpl(std::forward<T>(name),
std::is_integeral<T>()); // 不够正确
}
上面这个函数把它的形参转发给了logAndAddImpl
,但它还传递了另外一个实参,用来表示那个形参的类型T
是否为整型;如果传递给万能应用name
是个左值,那么T
会推导为左值引用。这意味着std::is_intergral<T>
在函数接受任意左值实参时,会得到结果“假”。std::remove_reference
会移除类型所附的一切引用饰词:
template<typename T>
void logAndAdd(T &&name) {
logAndAddImpl(std::forward<T>(name),
std::is_integral<typename std::remove_reference<T>::type>();
}
完成logAndAdd
后我们再来实现logAndAddImpl
。有连个重载版本,一个是只针对于非整型:
template<typename T>
void logAndAddImpl(T &&name, std::false_type) { // 非整型实参
auto now = std::chrono::system_clock()::now();
log(now, "logAndAdd");
names.emplaces(std::forward<T>(name));
}
如果T
是整型,则经由logAndAdd
传递给logAndAddImpl
的实参就会是一个继承自std::true_type
的对象,反之,则会是std::false_type
的对象。第二个重载包含了T
是整型的相反情况:
std::string nameFromIdx(int dx);
void logAndAddImpl(T &&name, std::false_type) { // 整型实参
logAndAdd(nemeFromIdx(idx));
}
上述设计中,类型std::false_type
和std::true_type
就是所谓的标签
对接受万能引用的模板施加限制
std::enable_if
可以强制编译器表现出来的行为如同特定模板不存在一般。
class Person{
template<typename T,
typename = typename std::enbale_if<condition>::type>
explicit Person(T &&n);
};
// 后续待补充。。。。