首页 > 代码库 > Effective Modern C++ 之 避免对以全局引用为参数的函数重载
Effective Modern C++ 之 避免对以全局引用为参数的函数重载
考察以下代码:
std::multiset<std::string> names; //global data structurevoid LogAndAdd(const std::string &name){ auto now = //got current time std::chrono::system_clock::now(); log(now, "logAndAdd"); //make log entry names.emplace(name);}
这看起来似乎没什么问题. 没错, 实际上这么编译也是可以通过的, 可是还存在优化的空间. 考察以下情况:
std::string petName = ("Darla");LogAndAdd(petName);LogAndAdd(std::string("Persephone"));LogAndAdd("Patty Dog");
其中, 只有第一个输入的参数是左值, 第二个是一个右值, 而第三个会先产生一个临时拷贝(所以说也是右值), 但是由于没有相应的函数对应, 调用这三次操作的开销都是拷贝的开销, 咱们本来是可以把后两个移动过去的对不? 所以可以这样:
template<typename T>void LogAndAdd(T &&name){ auto now = std::chono::system_clock::now(); log(now, "logAndAdd"); names.emplace(std::forward<T>(name));}
这下怎么样? 爽! 但是问题来了, 假如需求变了, 名字啥的需要通过索引来查找:
std::string NameFromIdx(int idx);void LogAndAdd(int idx){ auto now = std::chrono::system_clock::now(); log(now, "logAndAdd"); names.emplace(NameFromIdx(idx));}
对啊, 就是中间要多一道, 你这不是也实现了吗? 可是问题来了, 扭曲的用户可是什么都能做出来的, 如果你遇到这这么一个人. TA:
short nameIdx;...LogAndAdd(nameIdx);
那么问题就来了. 怎么, 因为 nameIdx 是个 short(短小) 所以参数为 int 的那个版本就不会接受它, 而使用模板的版本则以它开放的胸怀把它接受了, 当然结果就是一大长串莫名其妙的错误信息.
同样的, 假如有这么个类:
class Person{public: tempalte<typename T> explicit Person(T &&n) : _name(std::forward<T>(n)) {} explicit Person(int idx); Person(const Person&); Person(Person&&); ...};
看起来挺严谨, 实际上如果咱们这样:
Person p("Nancy"); //good nameauto cloneOfP(p); //WTF
对, 又出事了, 这个叫做 Nancy 的女士(不会是男的吧...) p, 人家是个非常量左值(额, non-const lvalue) 所以那几个非模板版本的移动/拷贝 构造函数根本就不知道这是个啥. 所以 Nancy 就被抛给用模板实现的版本了:
//就是这样class Person{public: tempalte<typename T> explicit Person(Person &n) : _name(std::forward<Person&>(n)) {} explicit Person(int idx); Person(const Person&); Person(Person&&); ...};
当然, 就像上面分析的, 当 Nancy 是个常量, 非模板版本的拷贝函数就来接家属了:
const Person p("Nancy");auto cloneOfP(p); //copy ctor
可问题是, 总不能项目里都是常量对象吧, 所以具体怎么处理这个棘手的问题, 请看下篇博客.
Effective Modern C++ 之 避免对以全局引用为参数的函数重载
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。