Effective Modern C++ 之 避免对以全局引用为参数的函数重载

考察以下代码:

std::multiset<std::string> names;           //global data structure

void 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 name
auto 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

可问题是, 总不能项目里都是常量对象吧, 所以具体怎么处理这个棘手的问题, 请看下篇博客.

posted @ 2015-01-05 00:48  wu_overflow  阅读(241)  评论(0编辑  收藏  举报