C++11函数重载最佳实践

12 篇文章 0 订阅

参数修饰情形

在C++11里,一个类型std::string有以下4种情形。

std::string s = "123"; // 值

const std::string s_c = "456"; // 不可修改的值

std::string& s_ref = s; // 引用

const std::string& s_cref = s; // 不可修改的引用

假如我们实现一个Test函数传入std::string作为参数,我们会遇到以下情形的调用:

Test(s);

Test(s_c);

Test(s_ref);

Test(s_cref);

另外再加上临时变量的场景。

Test(std::string("temp"));

最理想的情况下, 我们希望Test函数只需要写一遍, 即可完美匹配以上所有的类型加修饰。

参数的匹配规律

1.void Test(std::string str);

参数按值传递到函数,无论输入的std::string哪一种, 都会对输入参数做一次拷贝,再传递到函数内部处理。通用所有情形,缺点就是拷贝开销。

2.void Test(const std::string str);

与1几乎一模一样, 除了在函数内部无法修改std::string和调用std::string的非const函数。在std::string已经进行了拷贝的情况下, 拷贝的值只有函数内部用到, 保持const没有什么意义。

3.void Test(std::string& str);

只能匹配s和s_ref。 所有带const修饰, 或者临时变量, 都无法匹配到这个版本。但是这个版本有一个其他版本没有的特点,就是可以修改传进来的值。但是不建议使用, 后面会说到需要返回值的情况应该如何定义。

4.void Test(const std::string& str);

可以匹配所有情形, 并且无需拷贝。 从通用和效率来考虑,这种无疑是最高。

5.void Test(std::string&& str);

c++11新增的右值引用, 只能匹配临时变量的情形。合情合理, 右值引用本来就是为了临时变量而生。 这个版本的特点就是,str是在外部构造的,右值引用明确表达了, 外部不再需要用到str的了,str可以随便处置, 特别是str内部的一些资源, 可以直接拿来构造一个新的类, 避免对这些资源进行拷贝。 大部分情况下, 右值引用的版本都是为了提升性能而额外定义的,即使删掉函数, 系统依然能在其他版本的函数中匹配成功,通常是Test(const std::string& str),只是效率没那么高。

6.void Test(const std::string&& str);

与5几乎一模一样, 却又限制了str不能修改, 不能调用非const函数,有了5以后,6就没什么意义。

匹配冲突规律

1.void Test(std::string str) 和 其他所有冲突。

2.void Test(const std::string str) 和 其他所有冲突。

3.void Test(std::string& str) 和 void Test(const std::string& str)不冲突

当参数为std::string&时,void Test(std::string& str)优先匹配, 当它不存在时,

会匹配void Test(const std::string& str)版本。

4.void Test(std::string&& str);与void Test(std::string str);会冲突

void Test(std::string&& str);与void Test(const std::string str);会冲突

void Test(std::string&& str);与void Test(std::string& str);不冲突, 他们面向的场景完全不一样。

void Test(std::string&& str);与void Test(const std::string& str);不冲突, 当参数为临时变量, 或者std::move()包含的变量时, 优先匹配void Test(std::string&& str);。

5.void Test(const std::string&& str);没什么意义, 当void Test(std::string&& str);不存在,并且参数为临时变量, 或者std::move()包含的变量时,才会匹配到它。

重载最佳实践

1.对于输入参数来说, 永远使用void Test(const std::string& str);版本。

首先它能匹配所有情形, 其次不会对外部传入的参数做拷贝操作。 若真有拷贝需要,在函数内部拷贝一份便是。

2.对于某些对性能要求高的场景下,可在1的基础上,增加void Test(std::string&& str);版本。

3.避免使用void Test(std::string& str)版本,例如:

std::string str = "123";

Test(str);

在阅读以上代码时,Test函数到底有没有对str做过修改? 我们是无法一眼看得出来的。 当我们阅读这样的代码时, 每次遇到一个函数,都需要跳转到函数实现处查看才能确定,给大脑的思绪造成停顿, 对代码阅读,维护有十分具大的影响。

假如我们统一规定:

需要返回值给函数外部时, 只通过返回值, 或者指针的方式。

需要从外部传递值给内部时,统一通过const &的方式, 追求效率时再增加&&右值引用方式。

保持这种习惯, 我们代码的可读性将会上一个层次。

void Test(std::string* p_str);

std::string str = "123";

Test(&str);

Show(str);

AppendTo(&dst, src);

我们一眼就能看出, 函数需要哪些变量输入, 函数会修改到哪些变量。 逻辑瞬间清晰。

4.对于函数确定类型并且是int, double等基本类型, 可以不用const &的方式, 直接传值代码更加简洁, 并且基本类型拷贝成本低, 没有右值引用的需求。  唯独存在一种情形, 就是在模板的时候, 因为类型是不确定的, const&方式最能兼顾通用与效率, 它最优先。

举个例子

template<typename T>
T Add(const T& t1, const T& t2)
{
    return (t1 + t2);
}
T可能是int, 也可以是std::string, 也可以是其他自定义支持+操作符的类。

最佳使用const T&的声明方式。有更高的性能等需求,可以再通过进一步的模板特化去实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值