在函数返回值/range-for 等情况中
1、auto使用 有5种用法
auto :拷贝
auto& :左值引用,只能接左值(和常量右值)
auto&& :万能引用,能接左值和右值
const auto& :const 万能引用,能接左值和右值
const auto&& :常量右值引用,只能接右值
很多人直接就写成 auto&&,但尽量分场景使用
auto:用于你想修改右值的情形
auto&:用于你想修改左值的情形
auto&&:用于泛型编程中的转发
const auto&:用于只读
const auto&&:基本没用,基本可被 const auto& 替代(比 const auto& 多一个语义:一定得是右值。然而这没什么用,因为你都不对其进行修改,是左还是右没什么影响)
2、返回值自动推导,增强模板的泛型能力
//C++11
template<typename T1, typename T2>
auto sum(T1&& x, T2&& y) -> decltype(x + y) {
return x + y;
}
// C++14:
template<typename T1, typename T2>
auto sum(T1&& x, T2&& y) {
return x + y;
}
3、泛型Lambda
// C++14;
auto mul = [](const auto x, const auto y) { return x * y; };
4、decltype(auto)推导保留cv:
// C++14;
template<typename T, typename I>
decltype(auto) accessVector(T&& c, I i) { return c[i]; } // 返回 int&;
int main(int argc, char** argv) {
std::vector<int> v = {0};
accessVector(v, 0) = 10000;
std::cout << v.at(0) << std::endl; // 10000;
return 0;
}
5、做Perfect Forwarding 中对{}表达式的中转
void foo(std::vector<int> v) {
for (const auto& i : v) { std::cout << i << std::endl; }
}
template<typename ...Arg>
void forwardFunc(Arg&& ...args) {
foo(std::forward<Arg...>(args...));
}
int main(int argc, char** argv) {
auto il = {1, 2, 3};
forwardFunc(il);
return 0;
}
6、简化函数指针写法
int(*(*foo)())() {};
auto foo() -> auto (*)() -> int(*)() {}; // 与上述等价;
需要注意的几个坑
1、STL 代理类型导致 auto 可能推导出并非自己想要的类型:
// 主要源于 std::vector 在存储布尔元素时的特殊方式;
int main(int argc, char** argv) {
std::vector<bool> v ={ true };
auto x = v.at(0); // std::vector<bool>::reference, not bool;
return 0;
}
2、auto 在推导 List Initialization 表达式时的默认类型(std::initializer_list)
int main(int argc, char** argv) {
auto l = {1, 2, 3}; // std::initializer_list<int>;
}