条款三 了解decltype
基础知识
提供一个变量或者表达式,decltype会返回其类型,但是返回的内容会使人感到奇怪。
以下是一些简单的推断类型:
const int i = 0; // decltype(i) -> const int bool f(const Widget& w); // decltype(w) -> const Widget&, decltype(f) -> bool(const Widget&) struct Point { int x, y; } // decltype(Point::x) -> int Widget w; // decltype(w) -> Widget if(f(w)); // decltype(f(w)) -> bool template<typename T> class vector { public: T& operator[](std::size_t index); }; vector<int> v; // decltype(v) -> vector<int> if(v[0] == 0); // decltype(v[0]) -> int&
在C++11中,decltype的主要作用是推断根据形参类型推断返回类型。
对于std::vector<bool>,operator[]返回的并不是bool&,而是一个新的对象。
一种返回类型的推断的用法:
template<typename Container, typename Index> auto authAndAccess(Container& c, Index i) -> decltype(C[i]){ authenticateUser(); return c[i]; }
C++11允许允许单语句lambda返回类型的推断,C+14扩展到所有lambda和函数。
// C++14版本,但是会有问题 template<typename Container, typename Index> auto authAndAccess(Container& c, Index i){ authenticateUser(); return c[i]; }
以上函数虽然使用了auto,但套用auto的规则会出问题。如以上c[i]返回int&,但是根据推断规则会去掉引用类型,造成不能修改。
以下代码可以正确返回类型:
// C++14版本,可以正确返回类型 template<typename Container, typename Index> decltype(auto) authAndAccess(Container& c, Index i){ authenticateUser(); return c[i]; }
auto与decltype(auto)的区别:
Widget w; const Widget& cw = w; auto myWidget1 = cw; // myWidget1 -> Widget decltype(auto) myWidget2 = cw; // myWidget2 -> const Widget&
为了使得函数可以同时传入左值和右值,函数引入通用引用,正确形式如下:
// final C++14 version template<typename Container, typename Index> decltype(auto) authAndAccess(Container&& c, Index i){ authenticateUser(); return std::forward<Container>(c)[i]; } // final C++1 version template<typename Container, typename Index> auto authAndAccess(Container&& c, Index i) -> decltype(std::forward<Container>(c)[i]){ authenticateUser(); return std::forward<Container>(c)[i]; }
在使用decltype时,变量外加上括号会改变推断类型:
int x = 0; decltype(x); // -> int decltype((x)); // -> int&
总结
- decltype总是产生没经过修改的变量或表达式的类型
- 对于类型为T的左值表达式而非变量名,decltype总是返回T&类型
- C++14支持decltype(auto),其就像auto一样从初始化中推断类型,但是使用decltype的来推断类型