首页 > 代码库 > 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++ 之 避免对以全局引用为参数的函数重载